Some character devices may perform meaningful operations in their implementation of close() -- a good example would be socfpga.c which checks if the FPGA was programmed succesfully in it's close() method -- so ignoring return value of this call may cause false positives in checking exit status for success. Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
445 lines
8.2 KiB
C
445 lines
8.2 KiB
C
/*
|
|
* Copyright (c) 2014 Sascha Hauer <s.hauer@pengutronix.de>, 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 <common.h>
|
|
#include <fs.h>
|
|
#include <fcntl.h>
|
|
#include <malloc.h>
|
|
#include <libfile.h>
|
|
#include <progress.h>
|
|
#include <linux/stat.h>
|
|
|
|
/*
|
|
* 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, err1 = 0;
|
|
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)
|
|
err1 = close(dstfd);
|
|
|
|
return ret ?: err1;
|
|
}
|
|
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;
|
|
}
|