mirror of
https://github.com/troglobit/libite.git
synced 2026-11-02 13:10:33 +00:00
libite: Copy utility fixes and new functionality
- Add support for symlinks to copyfile() - Fix memory leak in copy_filep() - New function, fsendfile(), copy data between FILE streams Also, minor whitespace and coding style cleanup. Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
This commit is contained in:
1 parent
499f4d45d3
commit
bf9d1ec012
3 files changed
+86
-19
No files matched your search
+83
-17
@@ -41,6 +41,7 @@ static int __isdir_and_append_filename(char *src, char **dst);
|
|||||||
* @src: Source file.
|
* @src: Source file.
|
||||||
* @dst: Target file.
|
* @dst: Target file.
|
||||||
* @len: Number of bytes to copy, zero (0) for entire file.
|
* @len: Number of bytes to copy, zero (0) for entire file.
|
||||||
|
* @sym: Should symlinks be recreated (1) or followed (0)
|
||||||
*
|
*
|
||||||
* This is a C implementation of the command line cp(1) utility. It is one
|
* This is a C implementation of the command line cp(1) utility. It is one
|
||||||
* of the classic missing links in the UNIX C library. This version is from
|
* of the classic missing links in the UNIX C library. This version is from
|
||||||
@@ -54,10 +55,10 @@ static int __isdir_and_append_filename(char *src, char **dst);
|
|||||||
* may also indicate that @src was empty. If @src is a directory errno
|
* may also indicate that @src was empty. If @src is a directory errno
|
||||||
* will be set to %EISDIR since copyfile() is not recursive.
|
* will be set to %EISDIR since copyfile() is not recursive.
|
||||||
*/
|
*/
|
||||||
ssize_t copyfile(char *src, char *dst, int len)
|
ssize_t copyfile(char *src, char *dst, int len, int sym)
|
||||||
{
|
{
|
||||||
char *buffer;
|
char *buffer;
|
||||||
int s, d, n, isdir = 0, saved_errno = 0;
|
int s, d, n, result, isdir = 0, saved_errno = 0;
|
||||||
ssize_t size = 0;
|
ssize_t size = 0;
|
||||||
|
|
||||||
errno = 0; /* Reset before leaving this function. */
|
errno = 0; /* Reset before leaving this function. */
|
||||||
@@ -75,6 +76,26 @@ ssize_t copyfile(char *src, char *dst, int len)
|
|||||||
/* Check if target is a directory, then append the src base filename. */
|
/* Check if target is a directory, then append the src base filename. */
|
||||||
isdir = __isdir_and_append_filename(src, &dst);
|
isdir = __isdir_and_append_filename(src, &dst);
|
||||||
|
|
||||||
|
if (sym) {
|
||||||
|
if ((size = readlink(src, buffer, BUFSIZ)) > 0) {
|
||||||
|
if (size >= (ssize_t) BUFSIZ) {
|
||||||
|
free(buffer);
|
||||||
|
if (isdir)
|
||||||
|
free(dst);
|
||||||
|
errno = ENOBUFS;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer[size] = 0;
|
||||||
|
result = !symlink(buffer, dst);
|
||||||
|
free(buffer);
|
||||||
|
if (isdir)
|
||||||
|
free(dst);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Len == 0 means copy entire file */
|
/* Len == 0 means copy entire file */
|
||||||
if (len == 0) {
|
if (len == 0) {
|
||||||
/* XXX: This feels a bit unsafe, but 2047 MiB may be sufficient for us.
|
/* XXX: This feels a bit unsafe, but 2047 MiB may be sufficient for us.
|
||||||
@@ -83,18 +104,14 @@ ssize_t copyfile(char *src, char *dst, int len)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ((s = open(src, O_RDONLY)) >= 0) {
|
if ((s = open(src, O_RDONLY)) >= 0) {
|
||||||
if ((d =
|
if ((d = open(dst, O_WRONLY | O_CREAT | O_TRUNC, fmode(src))) >= 0) {
|
||||||
open(dst, O_WRONLY | O_CREAT | O_TRUNC,
|
|
||||||
fmode(src))) >= 0) {
|
|
||||||
do {
|
do {
|
||||||
int clen = len > BUFSIZ ? BUFSIZ : len;
|
int clen = len > BUFSIZ ? BUFSIZ : len;
|
||||||
|
|
||||||
if ((n = read(s, buffer, clen)) > 0) {
|
if ((n = read(s, buffer, clen)) > 0)
|
||||||
size += write(d, buffer, n);
|
size += write(d, buffer, n);
|
||||||
}
|
|
||||||
len -= clen;
|
len -= clen;
|
||||||
}
|
} while (len > 0 && n == BUFSIZ);
|
||||||
while (len > 0 && n == BUFSIZ);
|
|
||||||
|
|
||||||
close(d);
|
close(d);
|
||||||
} else {
|
} else {
|
||||||
@@ -140,7 +157,7 @@ int movefile(char *src, char *dst)
|
|||||||
if (rename(src, dst)) {
|
if (rename(src, dst)) {
|
||||||
if (errno == EXDEV) {
|
if (errno == EXDEV) {
|
||||||
errno = 0;
|
errno = 0;
|
||||||
copyfile(src, dst, 0);
|
copyfile(src, dst, 0, 0);
|
||||||
if (errno)
|
if (errno)
|
||||||
result = 1;
|
result = 1;
|
||||||
else
|
else
|
||||||
@@ -170,27 +187,30 @@ int movefile(char *src, char *dst)
|
|||||||
int copy_filep(FILE *src, FILE *dst)
|
int copy_filep(FILE *src, FILE *dst)
|
||||||
{
|
{
|
||||||
int result = 0;
|
int result = 0;
|
||||||
char *buf = (char *)malloc(BUFSIZ);
|
char *buf;
|
||||||
|
|
||||||
if (!src || !dst)
|
if (!src || !dst)
|
||||||
return EINVAL;
|
return EINVAL;
|
||||||
|
|
||||||
|
buf = (char *)malloc(BUFSIZ);
|
||||||
if (!buf)
|
if (!buf)
|
||||||
return errno;
|
return errno;
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
errno = 0;
|
errno = 0;
|
||||||
if (!fgets(buf, BUFSIZ, src)) {
|
if (!fgets(buf, BUFSIZ, src)) {
|
||||||
if (errno == EINTR) {
|
if (errno == EINTR) {
|
||||||
clearerr(src);
|
clearerr(src);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (feof(src))
|
if (feof(src))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
result = errno;
|
result = errno;
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (EOF == fputs(buf, dst)) {
|
while (EOF == fputs(buf, dst)) {
|
||||||
if (errno == EINTR) {
|
if (errno == EINTR) {
|
||||||
clearerr(dst);
|
clearerr(dst);
|
||||||
@@ -202,11 +222,57 @@ int copy_filep(FILE *src, FILE *dst)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
free(buf);
|
free(buf);
|
||||||
return errno = result;
|
return errno = result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fsendfile - copy data between file streams
|
||||||
|
* @out: Destination stream
|
||||||
|
* @in: Source stream
|
||||||
|
* @sz: Size in bytes to copy.
|
||||||
|
*
|
||||||
|
* @out may be NULL, in which case @sz bytes are read and discarded
|
||||||
|
* from @in. This is useful for streams where seeks are not
|
||||||
|
* permitted. Additionally @sz may be the special value zero (0) in
|
||||||
|
* which case fsendfile will copy until EOF is seen on @src.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* The number of bytes copied. If there was an error, -1 is returned
|
||||||
|
* and errno will be set accordingly.
|
||||||
|
*/
|
||||||
|
size_t fsendfile (FILE *out, FILE *in, size_t sz)
|
||||||
|
{
|
||||||
|
char *buf = malloc(BUFSIZ);
|
||||||
|
size_t blk = BUFSIZ, ret = 0, tot = 0;
|
||||||
|
|
||||||
|
if (!buf)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
while (!sz || tot < sz)
|
||||||
|
{
|
||||||
|
if (sz && ((sz - tot) < BUFSIZ))
|
||||||
|
blk = sz - tot;
|
||||||
|
|
||||||
|
ret = fread(buf, 1, blk, in);
|
||||||
|
if (ret <= 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (out && (fwrite(buf, ret, 1, out) != 1))
|
||||||
|
{
|
||||||
|
ret = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
tot += ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(buf);
|
||||||
|
|
||||||
|
return (ret == (size_t)-1)? (size_t)-1 : tot;
|
||||||
|
}
|
||||||
|
|
||||||
/* Tests if dst is a directory, if so, reallocates dst and appends src filename returning 1 */
|
/* Tests if dst is a directory, if so, reallocates dst and appends src filename returning 1 */
|
||||||
static int __isdir_and_append_filename(char *src, char **dst)
|
static int __isdir_and_append_filename(char *src, char **dst)
|
||||||
{
|
{
|
||||||
@@ -254,7 +320,7 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
printf("Copying file %s to %s\n", argv[1], argv[2]);
|
printf("Copying file %s to %s\n", argv[1], argv[2]);
|
||||||
|
|
||||||
return copyfile(argv[1], argv[2], 0);
|
return copyfile(argv[1], argv[2], 0, 0);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
int main(void)
|
int main(void)
|
||||||
@@ -293,7 +359,7 @@ int main(void)
|
|||||||
i = 0;
|
i = 0;
|
||||||
while (files[i]) {
|
while (files[i]) {
|
||||||
fprintf(stderr, "copyfile(%s, %s)\t", files[i], files[i + 1]);
|
fprintf(stderr, "copyfile(%s, %s)\t", files[i], files[i + 1]);
|
||||||
if (!copyfile(files[i], files[i + 1], 0))
|
if (!copyfile(files[i], files[i + 1], 0, 0))
|
||||||
perror("Failed");
|
perror("Failed");
|
||||||
|
|
||||||
if (fexist(files[i + 2]))
|
if (fexist(files[i + 2]))
|
||||||
@@ -312,7 +378,7 @@ int main(void)
|
|||||||
* Local Variables:
|
* Local Variables:
|
||||||
* compile-command: "gcc -g -I../include -o unittest -DUNITTEST copyfile.c && ./unittest"
|
* compile-command: "gcc -g -I../include -o unittest -DUNITTEST copyfile.c && ./unittest"
|
||||||
* version-control: t
|
* version-control: t
|
||||||
* kept-new-versions: 2
|
* indent-tabs-mode: t
|
||||||
* c-file-style: "ellemtel"
|
* c-file-style: "linux"
|
||||||
* End:
|
* End:
|
||||||
*/
|
*/
|
||||||
@@ -35,9 +35,10 @@ int fexist (char *file);
|
|||||||
int fisdir (char *file);
|
int fisdir (char *file);
|
||||||
mode_t fmode (char *file);
|
mode_t fmode (char *file);
|
||||||
|
|
||||||
ssize_t copyfile (char *src, char *dst, int len);
|
ssize_t copyfile (char *src, char *dst, int len, int sym);
|
||||||
int movefile (char *src, char *dst);
|
int movefile (char *src, char *dst);
|
||||||
int copy_filep (FILE *src, FILE *dst);
|
int copy_filep (FILE *src, FILE *dst);
|
||||||
|
size_t fsendfile (FILE *out, FILE *in, size_t sz);
|
||||||
|
|
||||||
int dir (const char *dir, const char *type, int (*filter) (const char *file), char ***list, int strip);
|
int dir (const char *dir, const char *type, int (*filter) (const char *file), char ***list, int strip);
|
||||||
int rsync (char *src, char *dst, int delete, int (*filter) (const char *file));
|
int rsync (char *src, char *dst, int delete, int (*filter) (const char *file));
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ int rsync(char *src, char *dst, int delete, int (*filter) (const char *file))
|
|||||||
void copy(char *src, char *dst)
|
void copy(char *src, char *dst)
|
||||||
{
|
{
|
||||||
errno = 0;
|
errno = 0;
|
||||||
copyfile(src, dst, 0);
|
copyfile(src, dst, 0, 1);
|
||||||
if (errno) {
|
if (errno) {
|
||||||
if (errno == EEXIST)
|
if (errno == EEXIST)
|
||||||
errno = 0;
|
errno = 0;
|
||||||
|
|||||||
Reference in new issue
Block a user