/* * Copyright (c) 2014 Sascha Hauer , Pengutronix * * See file CREDITS for list of people who contributed to this * project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ #include #include #include #include #include #include #include /* * write_full - write to filedescriptor * * Like write, but guarantees to write the full buffer out, else * it returns with an error. */ int write_full(int fd, void *buf, size_t size) { size_t insize = size; int now; while (size) { now = write(fd, buf, size); if (now <= 0) return now; size -= now; buf += now; } return insize; } EXPORT_SYMBOL(write_full); /* * read_full - read from filedescriptor * * Like read, but this function only returns less bytes than * requested when the end of file is reached. */ int read_full(int fd, void *buf, size_t size) { size_t insize = size; int now; int total = 0; while (size) { now = read(fd, buf, size); if (now == 0) return total; if (now < 0) return now; total += now; size -= now; buf += now; } return insize; } EXPORT_SYMBOL(read_full); /* * read_file_line - read a line from a file * * Used to compose a filename from a printf format and to read a line from this * file. All leading and trailing whitespaces (including line endings) are * removed. The returned buffer must be freed with free(). This function is * supposed for reading variable like content into a buffer, so files > 1024 * bytes are ignored. */ char *read_file_line(const char *fmt, ...) { va_list args; char *filename; char *buf, *line = NULL; size_t size; int ret; struct stat s; va_start(args, fmt); filename = vasprintf(fmt, args); va_end(args); ret = stat(filename, &s); if (ret) goto out; if (s.st_size > 1024) goto out; buf = read_file(filename, &size); if (!buf) goto out; line = strim(buf); line = xstrdup(line); free(buf); out: free(filename); return line; } EXPORT_SYMBOL_GPL(read_file_line); /** * read_file_2 - read a file to an allocated buffer * @filename: The filename to read * @size: After successful return contains the size of the file * @outbuf: contains a pointer to the file data after successful return * @max_size: The maximum size to read. Use FILESIZE_MAX for reading files * of any size. * * This function reads a file to an allocated buffer. At maximum @max_size * bytes are read. The actual read size is returned in @size. -EFBIG is * returned if the file is bigger than @max_size, but the buffer is read * anyway up to @max_size in this case. Free the buffer with free() after * usage. * * Return: 0 for success, or negative error code. -EFBIG is returned * when the file has been bigger than max_size. */ int read_file_2(const char *filename, size_t *size, void **outbuf, loff_t max_size) { int fd; struct stat s; void *buf = NULL; const char *tmpfile = "/.read_file_tmp"; int ret; loff_t read_size; again: ret = stat(filename, &s); if (ret) return ret; if (max_size == FILESIZE_MAX) read_size = s.st_size; else read_size = max_size; if (read_size == FILESIZE_MAX) { ret = copy_file(filename, tmpfile, 0); if (ret) return ret; filename = tmpfile; goto again; } buf = xzalloc(read_size + 1); fd = open(filename, O_RDONLY); if (fd < 0) { ret = fd; goto err_out; } ret = read_full(fd, buf, read_size); if (ret < 0) goto err_out1; close(fd); if (size) *size = ret; if (filename == tmpfile) unlink(tmpfile); *outbuf = buf; if (read_size < s.st_size) return -EFBIG; return 0; err_out1: close(fd); err_out: free(buf); if (filename == tmpfile) unlink(tmpfile); return ret; } EXPORT_SYMBOL(read_file_2); /** * read_file - read a file to an allocated buffer * @filename: The filename to read * @size: After successful return contains the size of the file * * This function reads a file to an allocated buffer. * Some TFTP servers do not transfer the size of a file. In this case * a the file is first read to a temporary file. * * Return: The buffer conataining the file or NULL on failure */ void *read_file(const char *filename, size_t *size) { int ret; void *buf; ret = read_file_2(filename, size, &buf, FILESIZE_MAX); if (!ret) return buf; return NULL; } EXPORT_SYMBOL(read_file); /** * write_file - write a buffer to a file * @filename: The filename to write * @size: The size of the buffer * * Return: 0 for success or negative error value */ int write_file(const char *filename, void *buf, size_t size) { int fd, ret; fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT); if (fd < 0) return fd; ret = write_full(fd, buf, size); close(fd); if (ret < 0) return ret; return 0; } EXPORT_SYMBOL(write_file); /** * copy_file - Copy a file * @src: The source filename * @dst: The destination filename * @verbose: if true, show a progression bar * * Return: 0 for success or negative error code */ int copy_file(const char *src, const char *dst, int verbose) { char *rw_buf = NULL; int srcfd = 0, dstfd = 0; int r, w; int ret = 1; void *buf; int total = 0; struct stat statbuf; rw_buf = xmalloc(RW_BUF_SIZE); srcfd = open(src, O_RDONLY); if (srcfd < 0) { printf("could not open %s: %s\n", src, errno_str()); goto out; } dstfd = open(dst, O_WRONLY | O_CREAT | O_TRUNC); if (dstfd < 0) { printf("could not open %s: %s\n", dst, errno_str()); goto out; } if (verbose) { if (stat(src, &statbuf) < 0) statbuf.st_size = 0; init_progression_bar(statbuf.st_size); } while (1) { r = read(srcfd, rw_buf, RW_BUF_SIZE); if (r < 0) { perror("read"); goto out; } if (!r) break; buf = rw_buf; while (r) { w = write(dstfd, buf, r); if (w < 0) { perror("write"); goto out; } buf += w; r -= w; total += w; } if (verbose) { if (statbuf.st_size && statbuf.st_size != FILESIZE_MAX) show_progress(total); else show_progress(total / 16384); } } ret = 0; out: if (verbose) putchar('\n'); free(rw_buf); if (srcfd > 0) close(srcfd); if (dstfd > 0) close(dstfd); return ret; } EXPORT_SYMBOL(copy_file); int copy_recursive(const char *src, const char *dst) { struct stat s; DIR *dir; struct dirent *d; int ret; ret = stat(src, &s); if (ret) return ret; if (!S_ISDIR(s.st_mode)) return copy_file(src, dst, 0); ret = make_directory(dst); if (ret) return ret; dir = opendir(src); if (!dir) return -EIO; while ((d = readdir(dir))) { char *from, *to; if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) continue; from = asprintf("%s/%s", src, d->d_name); to = asprintf("%s/%s", dst, d->d_name); ret = copy_recursive(from, to); if (ret) break; free(from); free(to); } closedir(dir); return ret; } /** * compare_file - Compare two files * @f1: The first file * @f2: The second file * * Return: 0 if both files are identical, 1 if they differ, * a negative error code if some error occured */ int compare_file(const char *f1, const char *f2) { int fd1, fd2, ret; struct stat s1, s2; void *buf1, *buf2; loff_t left; fd1 = open(f1, O_RDONLY); if (fd1 < 0) return -errno; fd2 = open(f2, O_RDONLY); if (fd2 < 0) { ret = -errno; goto err_out1; } ret = fstat(fd1, &s1); if (ret) goto err_out2; ret = fstat(fd2, &s2); if (ret) goto err_out2; if (s1.st_size != s2.st_size) return 1; buf1 = xmalloc(RW_BUF_SIZE); buf2 = xmalloc(RW_BUF_SIZE); left = s1.st_size; while (left) { loff_t now = min(left, (loff_t)RW_BUF_SIZE); ret = read_full(fd1, buf1, now); if (ret < 0) goto err_out3; ret = read_full(fd2, buf2, now); if (ret < 0) goto err_out3; if (memcmp(buf1, buf2, now)) { ret = 1; goto err_out3; } left -= now; } ret = 0; err_out3: free(buf1); free(buf2); err_out2: close(fd2); err_out1: close(fd1); return ret; }