9
0
Fork 0
barebox/lib/libfile.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;
else
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;
}