Relicense Sortix to the ISC license.
I hereby relicense all my work on Sortix under the ISC license as below.
All Sortix contributions by other people are already under this license,
are not substantial enough to be copyrightable, or have been removed.
All imported code from other projects is compatible with this license.
All GPL licensed code from other projects had previously been removed.
Copyright 2011-2016 Jonas 'Sortie' Termansen and contributors.
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
2016-03-02 17:38:16 -05:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2011, 2012, 2013, 2014, 2016 Jonas 'Sortie' Termansen.
|
|
|
|
*
|
|
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
|
|
* copyright notice and this permission notice appear in all copies.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
*
|
|
|
|
* cp.c
|
|
|
|
* Copy files and directories.
|
|
|
|
*/
|
2012-03-11 10:57:13 -04:00
|
|
|
|
2012-08-07 18:19:44 -04:00
|
|
|
#include <sys/stat.h>
|
2013-01-08 09:11:44 -05:00
|
|
|
#include <sys/types.h>
|
2012-08-07 18:19:44 -04:00
|
|
|
|
2013-01-08 09:11:44 -05:00
|
|
|
#include <dirent.h>
|
2016-09-18 18:57:22 -04:00
|
|
|
#include <err.h>
|
2011-11-22 11:26:47 -05:00
|
|
|
#include <errno.h>
|
2013-01-08 09:11:44 -05:00
|
|
|
#include <fcntl.h>
|
2016-02-28 18:40:20 -05:00
|
|
|
#include <stdbool.h>
|
2013-01-08 09:11:44 -05:00
|
|
|
#include <stddef.h>
|
|
|
|
#include <stdint.h>
|
2016-02-28 18:40:20 -05:00
|
|
|
#include <stdio.h>
|
2013-01-08 09:11:44 -05:00
|
|
|
#include <stdlib.h>
|
2011-11-22 11:26:47 -05:00
|
|
|
#include <string.h>
|
2013-01-08 09:11:44 -05:00
|
|
|
#include <unistd.h>
|
|
|
|
|
2016-09-18 18:57:22 -04:00
|
|
|
enum symbolic_dereference
|
2011-11-21 08:57:02 -05:00
|
|
|
{
|
2016-09-18 18:57:22 -04:00
|
|
|
SYMBOLIC_DEREFERENCE_NONE,
|
|
|
|
SYMBOLIC_DEREFERENCE_ARGUMENTS,
|
|
|
|
SYMBOLIC_DEREFERENCE_ALWAYS,
|
|
|
|
SYMBOLIC_DEREFERENCE_DEFAULT,
|
|
|
|
};
|
2011-11-21 08:57:02 -05:00
|
|
|
|
2016-01-31 19:45:12 -05:00
|
|
|
static const int FLAG_RECURSIVE = 1 << 0;
|
|
|
|
static const int FLAG_VERBOSE = 1 << 1;
|
|
|
|
static const int FLAG_TARGET_DIR = 1 << 2;
|
|
|
|
static const int FLAG_NO_TARGET_DIR = 1 << 3;
|
|
|
|
static const int FLAG_UPDATE = 1 << 4;
|
|
|
|
static const int FLAG_FORCE = 1 << 5;
|
2014-09-23 13:18:17 -04:00
|
|
|
|
2016-09-18 18:57:22 -04:00
|
|
|
static char* join_paths(const char* a, const char* b)
|
2014-09-23 13:18:17 -04:00
|
|
|
{
|
2016-09-18 18:57:22 -04:00
|
|
|
size_t a_len = strlen(a);
|
|
|
|
bool has_slash = (a_len && a[a_len-1] == '/') || b[0] == '/';
|
|
|
|
char* result;
|
|
|
|
if ( (has_slash && asprintf(&result, "%s%s", a, b) < 0) ||
|
|
|
|
(!has_slash && asprintf(&result, "%s/%s", a, b) < 0) )
|
|
|
|
return NULL;
|
|
|
|
return result;
|
|
|
|
}
|
2011-11-21 08:57:02 -05:00
|
|
|
|
2016-09-18 18:57:22 -04:00
|
|
|
static bool cp_contents(int srcfd, const char* srcpath,
|
|
|
|
int dstfd, const char* dstpath, int flags)
|
2013-01-08 09:11:44 -05:00
|
|
|
{
|
|
|
|
struct stat srcst, dstst;
|
|
|
|
if ( fstat(srcfd, &srcst) )
|
|
|
|
{
|
2016-09-18 18:57:22 -04:00
|
|
|
warn("stat: %s", srcpath);
|
2013-01-08 09:11:44 -05:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if ( fstat(dstfd, &dstst) )
|
|
|
|
{
|
2016-09-18 18:57:22 -04:00
|
|
|
warn("stat: %s", dstpath);
|
2013-01-08 09:11:44 -05:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if ( srcst.st_dev == dstst.st_dev && srcst.st_ino == dstst.st_ino )
|
|
|
|
{
|
2016-09-18 18:57:22 -04:00
|
|
|
warnx("`%s' and `%s' are the same file", srcpath, dstpath);
|
2013-01-08 09:11:44 -05:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if ( S_ISDIR(dstst.st_mode) )
|
|
|
|
{
|
2016-09-18 18:57:22 -04:00
|
|
|
warnx("cannot overwrite directory `%s' with non-directory", dstpath);
|
2013-01-08 09:11:44 -05:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if ( lseek(srcfd, 0, SEEK_SET) )
|
|
|
|
{
|
2016-09-18 18:57:22 -04:00
|
|
|
warn("can't seek: %s", srcpath);
|
2013-01-08 09:11:44 -05:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if ( lseek(dstfd, 0, SEEK_SET) )
|
|
|
|
{
|
2016-09-18 18:57:22 -04:00
|
|
|
warn("can't seek: %s", dstpath);
|
2013-01-08 09:11:44 -05:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if ( flags & FLAG_VERBOSE )
|
|
|
|
printf("`%s' -> `%s'\n", srcpath, dstpath);
|
2016-09-18 18:57:22 -04:00
|
|
|
if ( ftruncate(dstfd, srcst.st_size) < 0 )
|
|
|
|
{
|
|
|
|
warn("truncate: %s", dstpath);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
static unsigned char buffer[64 * 1024];
|
2013-01-08 09:11:44 -05:00
|
|
|
while ( true )
|
|
|
|
{
|
2016-09-18 18:57:22 -04:00
|
|
|
ssize_t numbytes = read(srcfd, buffer, sizeof(buffer));
|
2013-01-08 09:11:44 -05:00
|
|
|
if ( numbytes == 0 )
|
|
|
|
break;
|
|
|
|
if ( numbytes < 0 )
|
|
|
|
{
|
2016-09-18 18:57:22 -04:00
|
|
|
warn("read: %s", srcpath);
|
2013-01-08 09:11:44 -05:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
size_t sofar = 0;
|
|
|
|
while ( sofar < (size_t) numbytes )
|
|
|
|
{
|
2016-09-18 18:57:22 -04:00
|
|
|
size_t left = numbytes - sofar;
|
|
|
|
ssize_t amount = write(dstfd, buffer + sofar, left);
|
2013-01-08 09:11:44 -05:00
|
|
|
if ( amount <= 0 )
|
|
|
|
{
|
2016-09-18 18:57:22 -04:00
|
|
|
warn("write: %s", dstpath);
|
2013-01-08 09:11:44 -05:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
sofar += amount;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
2012-08-07 18:19:44 -04:00
|
|
|
|
2016-09-18 18:57:22 -04:00
|
|
|
static bool cp(int srcdirfd, const char* srcrel, const char* srcpath,
|
|
|
|
int dstdirfd, const char* dstrel, const char* dstpath,
|
|
|
|
int flags, enum symbolic_dereference symbolic_dereference)
|
2013-01-08 09:11:44 -05:00
|
|
|
{
|
|
|
|
struct stat srcst;
|
2016-09-18 18:57:22 -04:00
|
|
|
int deref_flags = O_RDONLY;
|
2014-09-23 13:18:17 -04:00
|
|
|
if ( symbolic_dereference == SYMBOLIC_DEREFERENCE_NONE )
|
2016-09-18 18:57:22 -04:00
|
|
|
deref_flags |= O_NOFOLLOW;
|
|
|
|
int srcfd = openat(srcdirfd, srcrel, O_RDONLY | deref_flags);
|
|
|
|
if ( srcfd < 0 &&
|
|
|
|
symbolic_dereference == SYMBOLIC_DEREFERENCE_NONE &&
|
|
|
|
errno == ELOOP )
|
|
|
|
{
|
|
|
|
// TODO: How to handle the update flag in this case?
|
|
|
|
struct stat srcst;
|
|
|
|
if ( fstatat(srcdirfd, srcrel, &srcst, AT_SYMLINK_NOFOLLOW) < 0 )
|
|
|
|
{
|
|
|
|
warn("%s", srcpath);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if ( (uintmax_t) SIZE_MAX <= (uintmax_t) srcst.st_size )
|
|
|
|
{
|
|
|
|
errno = EOVERFLOW;
|
|
|
|
warn("%s", srcpath);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
size_t size = (size_t) srcst.st_size + 1;
|
|
|
|
char* content = (char*) malloc(size);
|
|
|
|
if ( !content )
|
|
|
|
{
|
|
|
|
warn("malloc: %s", srcpath);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
ssize_t amount = readlinkat(srcdirfd, srcrel, content, size);
|
|
|
|
if ( amount < 0 )
|
|
|
|
{
|
|
|
|
warn("readlink: %s", srcpath);
|
|
|
|
free(content);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if ( size <= (size_t) amount )
|
|
|
|
{
|
|
|
|
errno = EOVERFLOW;
|
|
|
|
warn("readlink: %s", srcpath);
|
|
|
|
free(content);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
content[amount] = '\0';
|
|
|
|
int ret = symlinkat(content, dstdirfd, dstrel);
|
|
|
|
if ( ret < 0 && errno == EEXIST )
|
|
|
|
{
|
|
|
|
if ( unlinkat(dstdirfd, dstrel, 0) == 0 )
|
|
|
|
{
|
|
|
|
if ( flags & FLAG_VERBOSE )
|
|
|
|
printf("removed `%s'\n", dstpath);
|
|
|
|
ret = symlinkat(content, dstdirfd, dstrel);
|
|
|
|
}
|
|
|
|
else if ( errno == EISDIR )
|
|
|
|
{
|
|
|
|
warnx("cannot overwrite directory `%s' with non-directory",
|
|
|
|
dstpath);
|
|
|
|
free(content);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
warn("unlink: %s", dstpath);
|
|
|
|
free(content);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ( ret < 0 )
|
|
|
|
{
|
|
|
|
warn("symlink: %s", dstpath);
|
|
|
|
free(content);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
free(content);
|
|
|
|
return true;
|
|
|
|
}
|
2013-01-08 09:11:44 -05:00
|
|
|
if ( srcfd < 0 )
|
2011-11-21 08:57:02 -05:00
|
|
|
{
|
2016-09-18 18:57:22 -04:00
|
|
|
warn("%s", srcpath);
|
2014-06-27 16:46:50 -04:00
|
|
|
return false;
|
2013-01-08 09:11:44 -05:00
|
|
|
}
|
|
|
|
if ( fstat(srcfd, &srcst) )
|
|
|
|
{
|
2016-09-18 18:57:22 -04:00
|
|
|
warn("stat: %s", srcpath);
|
2014-06-27 16:46:50 -04:00
|
|
|
return close(srcfd), false;
|
2013-01-08 09:11:44 -05:00
|
|
|
}
|
2014-09-23 13:18:17 -04:00
|
|
|
if ( symbolic_dereference == SYMBOLIC_DEREFERENCE_ARGUMENTS )
|
|
|
|
symbolic_dereference = SYMBOLIC_DEREFERENCE_NONE;
|
2016-09-18 18:57:22 -04:00
|
|
|
if ( S_ISDIR(srcst.st_mode) )
|
2014-09-23 13:18:17 -04:00
|
|
|
{
|
2016-09-18 18:57:22 -04:00
|
|
|
if ( !(flags & FLAG_RECURSIVE) )
|
2014-09-23 13:18:17 -04:00
|
|
|
{
|
2016-09-18 18:57:22 -04:00
|
|
|
warnx("omitting directory `%s'", srcpath);
|
2014-09-23 13:18:17 -04:00
|
|
|
return close(srcfd), false;
|
|
|
|
}
|
2016-09-18 18:57:22 -04:00
|
|
|
int dstfd = openat(dstdirfd, dstrel, O_RDONLY | O_DIRECTORY);
|
|
|
|
if ( dstfd < 0 )
|
2014-09-23 13:18:17 -04:00
|
|
|
{
|
2016-09-18 18:57:22 -04:00
|
|
|
if ( errno != ENOENT )
|
2014-09-23 13:18:17 -04:00
|
|
|
{
|
2016-09-18 18:57:22 -04:00
|
|
|
warn("%s", dstpath);
|
2014-09-23 13:18:17 -04:00
|
|
|
return close(srcfd), false;
|
|
|
|
}
|
2016-09-18 18:57:22 -04:00
|
|
|
if ( mkdirat(dstdirfd, dstrel, srcst.st_mode & 03777) )
|
2014-09-23 13:18:17 -04:00
|
|
|
{
|
2016-09-18 18:57:22 -04:00
|
|
|
warn("cannot create directory `%s'", dstpath);
|
|
|
|
return close(srcfd), false;
|
2014-09-23 13:18:17 -04:00
|
|
|
}
|
2016-09-18 18:57:22 -04:00
|
|
|
int dstfdflags = O_RDONLY | O_DIRECTORY;
|
|
|
|
if ( (dstfd = openat(dstdirfd, dstrel, dstfdflags)) < 0 )
|
2014-09-23 13:18:17 -04:00
|
|
|
{
|
2016-09-18 18:57:22 -04:00
|
|
|
warn("%s", dstpath);
|
2014-09-23 13:18:17 -04:00
|
|
|
return close(srcfd), false;
|
|
|
|
}
|
|
|
|
}
|
2016-09-18 18:57:22 -04:00
|
|
|
struct stat srcst, dstst;
|
|
|
|
if ( fstat(srcfd, &srcst) < 0 )
|
2014-09-23 13:18:17 -04:00
|
|
|
{
|
2016-09-18 18:57:22 -04:00
|
|
|
warn("stat: %s", srcpath);
|
|
|
|
return close(dstfd), close(srcfd), false;
|
2014-09-23 13:18:17 -04:00
|
|
|
}
|
|
|
|
if ( fstat(dstfd, &dstst) < 0 )
|
|
|
|
{
|
2016-09-18 18:57:22 -04:00
|
|
|
warn("stat: %s", dstpath);
|
2014-09-23 13:18:17 -04:00
|
|
|
return close(dstfd), close(srcfd), false;
|
|
|
|
}
|
2016-09-18 18:57:22 -04:00
|
|
|
if ( srcst.st_dev == dstst.st_dev && srcst.st_ino == dstst.st_ino )
|
2014-09-23 13:18:17 -04:00
|
|
|
{
|
2016-09-18 18:57:22 -04:00
|
|
|
warnx("error: `%s' and `%s' are the same file", srcpath, dstpath);
|
2014-09-23 13:18:17 -04:00
|
|
|
return close(dstfd), close(srcfd), false;
|
|
|
|
}
|
2016-09-18 18:57:22 -04:00
|
|
|
DIR* srcdir = fdopendir(srcfd);
|
|
|
|
if ( !srcdir )
|
|
|
|
return warn("fdopendir: %s", srcpath), false;
|
|
|
|
if ( flags & FLAG_VERBOSE )
|
|
|
|
printf("`%s' -> `%s'\n", srcpath, dstpath);
|
|
|
|
bool ret = true;
|
|
|
|
struct dirent* entry;
|
|
|
|
while ( (errno = 0, entry = readdir(srcdir)) )
|
2013-01-08 09:11:44 -05:00
|
|
|
{
|
2016-09-18 18:57:22 -04:00
|
|
|
const char* name = entry->d_name;
|
|
|
|
if ( !strcmp(name, ".") || !strcmp(name, "..") )
|
|
|
|
continue;
|
|
|
|
char* srcpath_new = join_paths(srcpath, name);
|
|
|
|
if ( !srcpath_new )
|
|
|
|
err(1, "malloc");
|
|
|
|
char* dstpath_new = join_paths(dstpath, name);
|
|
|
|
if ( !dstpath_new )
|
|
|
|
err(1, "malloc");
|
|
|
|
bool ok = cp(dirfd(srcdir), name, srcpath_new,
|
|
|
|
dstfd, name, dstpath_new,
|
|
|
|
flags, symbolic_dereference);
|
|
|
|
free(srcpath_new);
|
|
|
|
free(dstpath_new);
|
|
|
|
ret = ret && ok;
|
2013-01-08 09:11:44 -05:00
|
|
|
}
|
2016-09-18 18:57:22 -04:00
|
|
|
if ( errno )
|
2013-01-08 09:11:44 -05:00
|
|
|
{
|
2016-09-18 18:57:22 -04:00
|
|
|
warn("readdir: %s", srcpath);
|
|
|
|
ret = false;
|
2013-01-08 09:11:44 -05:00
|
|
|
}
|
|
|
|
close(dstfd);
|
2016-09-18 18:57:22 -04:00
|
|
|
closedir(srcdir);
|
|
|
|
return ret;
|
2013-01-08 09:11:44 -05:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-06-27 16:46:50 -04:00
|
|
|
if ( flags & FLAG_UPDATE )
|
|
|
|
{
|
|
|
|
struct stat dstst;
|
2016-09-18 18:57:22 -04:00
|
|
|
if ( fstatat(dstdirfd, dstrel, &dstst, 0) == 0 &&
|
|
|
|
S_ISREG(dstst.st_mode) )
|
2014-06-27 16:46:50 -04:00
|
|
|
{
|
2016-09-18 18:57:22 -04:00
|
|
|
if ( srcst.st_mtim.tv_sec < dstst.st_mtim.tv_sec ||
|
|
|
|
(srcst.st_mtim.tv_sec == dstst.st_mtim.tv_sec &&
|
|
|
|
srcst.st_mtim.tv_nsec <= dstst.st_mtim.tv_nsec) )
|
2014-06-27 16:46:50 -04:00
|
|
|
return close(srcfd), true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-31 19:45:12 -05:00
|
|
|
int oflags = O_WRONLY | O_CREAT;
|
|
|
|
mode_t omode = srcst.st_mode & 03777;
|
|
|
|
int dstfd = openat(dstdirfd, dstrel, oflags, omode);
|
|
|
|
if ( dstfd < 0 &&
|
2016-09-18 18:57:22 -04:00
|
|
|
flags & FLAG_FORCE &&
|
2016-01-31 19:45:12 -05:00
|
|
|
faccessat(dstdirfd, dstrel, F_OK, AT_SYMLINK_NOFOLLOW) == 0 )
|
|
|
|
{
|
|
|
|
if ( unlinkat(dstdirfd, dstrel, 0) < 0 )
|
|
|
|
{
|
2016-09-18 18:57:22 -04:00
|
|
|
warn("%s", dstpath);
|
2016-01-31 19:45:12 -05:00
|
|
|
return close(srcfd), false;
|
|
|
|
}
|
|
|
|
dstfd = openat(dstdirfd, dstrel, oflags, omode);
|
|
|
|
}
|
2013-01-08 09:11:44 -05:00
|
|
|
if ( dstfd < 0 )
|
2012-03-24 10:34:30 -04:00
|
|
|
{
|
2016-09-18 18:57:22 -04:00
|
|
|
warn("%s", dstpath);
|
2014-06-27 16:46:50 -04:00
|
|
|
return close(srcfd), false;
|
2012-03-24 10:34:30 -04:00
|
|
|
}
|
2016-09-18 18:57:22 -04:00
|
|
|
bool ret = cp_contents(srcfd, srcpath, dstfd, dstpath, flags);
|
2013-01-08 09:11:44 -05:00
|
|
|
close(dstfd);
|
2016-09-18 18:57:22 -04:00
|
|
|
close(srcfd);
|
|
|
|
return ret;
|
2011-11-21 08:57:02 -05:00
|
|
|
}
|
2013-01-08 09:11:44 -05:00
|
|
|
}
|
|
|
|
|
2016-09-18 18:57:22 -04:00
|
|
|
static
|
|
|
|
bool cp_directory(int srcdirfd, const char* srcrel, const char* srcpath,
|
|
|
|
int dstdirfd, const char* dstrel, const char* dstpath,
|
|
|
|
int flags, enum symbolic_dereference symbolic_dereference)
|
2013-01-08 09:11:44 -05:00
|
|
|
{
|
2016-09-18 18:57:22 -04:00
|
|
|
size_t srcrel_last_slash = strlen(srcrel);
|
|
|
|
while ( srcrel_last_slash && srcrel[srcrel_last_slash-1] != '/' )
|
|
|
|
srcrel_last_slash--;
|
|
|
|
const char* src_basename = srcrel + srcrel_last_slash;
|
2013-01-08 09:11:44 -05:00
|
|
|
int dstfd = openat(dstdirfd, dstrel, O_RDONLY | O_DIRECTORY);
|
|
|
|
if ( dstfd < 0 )
|
|
|
|
{
|
2016-09-18 18:57:22 -04:00
|
|
|
warn("%s", dstpath);
|
2013-01-08 09:11:44 -05:00
|
|
|
return false;
|
|
|
|
}
|
2016-09-18 18:57:22 -04:00
|
|
|
char* dstpath_new = join_paths(dstpath, src_basename);
|
|
|
|
if ( !dstpath_new )
|
|
|
|
err(1, "malloc");
|
|
|
|
bool ret = cp(srcdirfd, srcrel, srcpath,
|
|
|
|
dstfd, src_basename, dstpath_new,
|
|
|
|
flags, symbolic_dereference);
|
2013-01-08 09:11:44 -05:00
|
|
|
free(dstpath_new);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-09-18 18:57:22 -04:00
|
|
|
static
|
|
|
|
bool cp_ambigious(int srcdirfd, const char* srcrel, const char* srcpath,
|
|
|
|
int dstdirfd, const char* dstrel, const char* dstpath,
|
|
|
|
int flags, enum symbolic_dereference symbolic_dereference)
|
2013-01-08 09:11:44 -05:00
|
|
|
{
|
|
|
|
struct stat dstst;
|
2014-06-27 16:46:50 -04:00
|
|
|
if ( fstatat(dstdirfd, dstrel, &dstst, 0) < 0 )
|
2013-01-08 09:11:44 -05:00
|
|
|
{
|
|
|
|
if ( errno != ENOENT )
|
|
|
|
{
|
2016-09-18 18:57:22 -04:00
|
|
|
warn("%s", dstpath);
|
2013-01-08 09:11:44 -05:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
dstst.st_mode = S_IFREG;
|
|
|
|
}
|
|
|
|
if ( S_ISDIR(dstst.st_mode) )
|
2016-09-18 18:57:22 -04:00
|
|
|
return cp_directory(srcdirfd, srcrel, srcpath,
|
|
|
|
dstdirfd, dstrel, dstpath,
|
|
|
|
flags, symbolic_dereference);
|
2013-01-08 09:11:44 -05:00
|
|
|
else
|
2016-09-18 18:57:22 -04:00
|
|
|
return cp(srcdirfd, srcrel, srcpath,
|
|
|
|
dstdirfd, dstrel, dstpath,
|
|
|
|
flags, symbolic_dereference);
|
2013-01-08 09:11:44 -05:00
|
|
|
}
|
|
|
|
|
2015-07-29 19:08:49 -04:00
|
|
|
static void compact_arguments(int* argc, char*** argv)
|
2014-06-27 16:46:50 -04:00
|
|
|
{
|
|
|
|
for ( int i = 0; i < *argc; i++ )
|
|
|
|
{
|
|
|
|
while ( i < *argc && !(*argv)[i] )
|
|
|
|
{
|
|
|
|
for ( int n = i; n < *argc; n++ )
|
|
|
|
(*argv)[n] = (*argv)[n+1];
|
|
|
|
(*argc)--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-01-08 09:11:44 -05:00
|
|
|
int main(int argc, char* argv[])
|
|
|
|
{
|
2014-06-27 16:46:50 -04:00
|
|
|
int flags = 0;
|
|
|
|
const char* target_directory = NULL;
|
2014-08-02 06:15:29 -04:00
|
|
|
const char* preserve_list = NULL;
|
2014-09-23 13:18:17 -04:00
|
|
|
enum symbolic_dereference symbolic_dereference = SYMBOLIC_DEREFERENCE_DEFAULT;
|
2013-01-08 09:11:44 -05:00
|
|
|
for ( int i = 1; i < argc; i++ )
|
|
|
|
{
|
|
|
|
const char* arg = argv[i];
|
2015-07-29 19:08:49 -04:00
|
|
|
if ( arg[0] != '-' || !arg[1] )
|
2013-01-08 09:11:44 -05:00
|
|
|
continue;
|
|
|
|
argv[i] = NULL;
|
|
|
|
if ( !strcmp(arg, "--") )
|
|
|
|
break;
|
|
|
|
if ( arg[1] != '-' )
|
|
|
|
{
|
2016-02-28 18:40:20 -05:00
|
|
|
char c;
|
|
|
|
while ( (c = *++arg) ) switch ( c )
|
2013-01-08 09:11:44 -05:00
|
|
|
{
|
2014-09-08 11:42:38 -04:00
|
|
|
#ifdef CP_PRETEND_TO_BE_INSTALL
|
|
|
|
case 'b': /* ignored */ break;
|
|
|
|
case 'c': /* ignored */ break;
|
|
|
|
case 'C': /* ignored */ break;
|
|
|
|
case 'd': /* ignored */ break;
|
|
|
|
case 'g': if ( *(arg + 1) ) arg = "g"; else if ( i + 1 != argc ) argv[++i] = NULL; break;
|
|
|
|
case 'm': if ( *(arg + 1) ) arg = "m"; else if ( i + 1 != argc ) argv[++i] = NULL; break;
|
|
|
|
case 'o': if ( *(arg + 1) ) arg = "o"; else if ( i + 1 != argc ) argv[++i] = NULL; break;
|
|
|
|
case 's': /* ignored */ break;
|
|
|
|
#endif
|
2016-01-31 19:45:12 -05:00
|
|
|
case 'f': flags |= FLAG_FORCE; break;
|
2014-09-23 13:18:17 -04:00
|
|
|
case 'H': symbolic_dereference = SYMBOLIC_DEREFERENCE_ARGUMENTS; break;
|
|
|
|
case 'L': symbolic_dereference = SYMBOLIC_DEREFERENCE_ALWAYS; break;
|
2013-01-08 09:11:44 -05:00
|
|
|
case 'r':
|
|
|
|
case 'R': flags |= FLAG_RECURSIVE; break;
|
|
|
|
case 'v': flags |= FLAG_VERBOSE; break;
|
2014-06-27 16:46:50 -04:00
|
|
|
case 't':
|
|
|
|
flags |= FLAG_TARGET_DIR;
|
|
|
|
if ( *(arg + 1) )
|
|
|
|
target_directory = arg + 1;
|
|
|
|
else if ( i + 1 == argc )
|
2016-09-18 18:57:22 -04:00
|
|
|
errx(1, "option requires an argument -- '%c'", c);
|
2014-06-27 16:46:50 -04:00
|
|
|
else
|
|
|
|
{
|
|
|
|
target_directory = argv[i+1];
|
|
|
|
argv[++i] = NULL;
|
|
|
|
}
|
|
|
|
arg = "t";
|
|
|
|
break;
|
2013-01-08 09:11:44 -05:00
|
|
|
case 'T': flags |= FLAG_NO_TARGET_DIR; break;
|
|
|
|
case 'u': flags |= FLAG_UPDATE; break;
|
2014-08-02 06:15:29 -04:00
|
|
|
case 'p': preserve_list = "mode,ownership,timestamps"; break;
|
2014-09-23 13:18:17 -04:00
|
|
|
case 'P': symbolic_dereference = SYMBOLIC_DEREFERENCE_NONE; break;
|
2013-01-08 09:11:44 -05:00
|
|
|
default:
|
2013-02-19 15:41:53 -05:00
|
|
|
#ifdef CP_PRETEND_TO_BE_INSTALL
|
2016-09-18 18:57:22 -04:00
|
|
|
fprintf(stderr, "%s (fake): unknown option, ignoring -- '%c'\n",
|
|
|
|
argv[0], c);
|
2013-02-19 15:41:53 -05:00
|
|
|
continue;
|
|
|
|
#endif
|
2016-09-18 18:57:22 -04:00
|
|
|
errx(1, "unknown option -- '%c'", c);
|
2013-01-08 09:11:44 -05:00
|
|
|
}
|
|
|
|
}
|
2014-06-27 16:46:50 -04:00
|
|
|
else if ( !strcmp(arg, "--dereference") )
|
2014-09-23 13:18:17 -04:00
|
|
|
symbolic_dereference = SYMBOLIC_DEREFERENCE_ALWAYS;
|
2013-01-08 09:11:44 -05:00
|
|
|
else if ( !strcmp(arg, "--recursive") )
|
|
|
|
flags |= FLAG_RECURSIVE;
|
|
|
|
else if ( !strcmp(arg, "--verbose") )
|
|
|
|
flags |= FLAG_VERBOSE;
|
2014-08-02 06:15:29 -04:00
|
|
|
else if ( !strcmp(arg, "--preserve") )
|
|
|
|
preserve_list = "mode,ownership,timestamps";
|
|
|
|
else if ( !strncmp(arg, "--preserve=", strlen("--preserve=")) )
|
|
|
|
preserve_list = arg + strlen("--preserve=");
|
2013-01-08 09:11:44 -05:00
|
|
|
else if ( !strcmp(arg, "--target-directory") )
|
2014-06-27 16:46:50 -04:00
|
|
|
{
|
|
|
|
if ( i + 1 == argc )
|
2016-09-18 18:57:22 -04:00
|
|
|
errx(1, "option '--target-directory' requires an argument");
|
2014-06-27 16:46:50 -04:00
|
|
|
target_directory = argv[++i];
|
|
|
|
argv[i] = NULL;
|
2013-01-08 09:11:44 -05:00
|
|
|
flags |= FLAG_TARGET_DIR;
|
2014-06-27 16:46:50 -04:00
|
|
|
}
|
|
|
|
else if ( !strncmp(arg, "--target-directory=", strlen("--target-directory=")) )
|
|
|
|
{
|
|
|
|
target_directory = arg + strlen("--target-directory=");
|
|
|
|
flags |= FLAG_TARGET_DIR;
|
|
|
|
}
|
2013-01-08 09:11:44 -05:00
|
|
|
else if ( !strcmp(arg, "--no-target-directory") )
|
|
|
|
flags |= FLAG_NO_TARGET_DIR;
|
|
|
|
else if ( !strcmp(arg, "--update") )
|
|
|
|
flags |= FLAG_UPDATE;
|
|
|
|
else if ( !strcmp(arg, "--no-dereference") )
|
2014-09-23 13:18:17 -04:00
|
|
|
symbolic_dereference = SYMBOLIC_DEREFERENCE_NONE;
|
2013-01-08 09:11:44 -05:00
|
|
|
else
|
|
|
|
{
|
2013-02-19 15:41:53 -05:00
|
|
|
#ifdef CP_PRETEND_TO_BE_INSTALL
|
2016-09-18 18:57:22 -04:00
|
|
|
fprintf(stderr, "%s (fake): unknown option, ignoring: %s\n", argv[0], arg);
|
2013-02-19 15:41:53 -05:00
|
|
|
continue;
|
|
|
|
#endif
|
2016-09-18 18:57:22 -04:00
|
|
|
errx(1, "unknown option: %s", arg);
|
2013-01-08 09:11:44 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-27 16:46:50 -04:00
|
|
|
if ( (flags & FLAG_TARGET_DIR) && (flags & FLAG_NO_TARGET_DIR) )
|
2016-09-18 18:57:22 -04:00
|
|
|
errx(1, "cannot combine --target-directory (-t) and --no-target-directory (-T)");
|
2013-01-08 09:11:44 -05:00
|
|
|
|
2014-09-23 13:18:17 -04:00
|
|
|
if ( symbolic_dereference == SYMBOLIC_DEREFERENCE_DEFAULT )
|
|
|
|
{
|
|
|
|
if ( flags & FLAG_RECURSIVE )
|
|
|
|
symbolic_dereference = SYMBOLIC_DEREFERENCE_NONE;
|
|
|
|
else
|
|
|
|
symbolic_dereference = SYMBOLIC_DEREFERENCE_ALWAYS;
|
|
|
|
}
|
|
|
|
|
2014-08-02 06:15:29 -04:00
|
|
|
// TODO: Actually preserve.
|
|
|
|
(void) preserve_list;
|
|
|
|
|
2014-06-27 16:46:50 -04:00
|
|
|
compact_arguments(&argc, &argv);
|
2013-01-08 09:11:44 -05:00
|
|
|
|
2014-06-27 16:46:50 -04:00
|
|
|
if ( argc < 2 )
|
2016-09-18 18:57:22 -04:00
|
|
|
errx(1, "missing file operand");
|
2014-06-27 16:46:50 -04:00
|
|
|
|
|
|
|
if ( flags & FLAG_NO_TARGET_DIR )
|
2013-01-08 09:11:44 -05:00
|
|
|
{
|
|
|
|
const char* src = argv[1];
|
|
|
|
if ( argc < 3 )
|
2016-09-18 18:57:22 -04:00
|
|
|
errx(1, "missing destination file operand after `%s'", src);
|
2013-01-08 09:11:44 -05:00
|
|
|
const char* dst = argv[2];
|
|
|
|
if ( 3 < argc )
|
2016-09-18 18:57:22 -04:00
|
|
|
errx(1, "extra operand `%s'", argv[3]);
|
|
|
|
return cp(AT_FDCWD, src, src,
|
|
|
|
AT_FDCWD, dst, dst,
|
|
|
|
flags, symbolic_dereference) ? 0 : 1;
|
2013-01-08 09:11:44 -05:00
|
|
|
}
|
|
|
|
|
2014-06-27 16:46:50 -04:00
|
|
|
if ( !(flags & FLAG_TARGET_DIR) && argc <= 3 )
|
|
|
|
{
|
|
|
|
const char* src = argv[1];
|
|
|
|
if ( argc < 3 )
|
2016-09-18 18:57:22 -04:00
|
|
|
errx(1, "missing destination file operand after `%s'", src);
|
2014-06-27 16:46:50 -04:00
|
|
|
const char* dst = argv[2];
|
2016-09-18 18:57:22 -04:00
|
|
|
return cp_ambigious(AT_FDCWD, src, src,
|
|
|
|
AT_FDCWD, dst, dst,
|
|
|
|
flags, symbolic_dereference) ? 0 : 1;
|
2014-06-27 16:46:50 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if ( !target_directory )
|
|
|
|
{
|
|
|
|
target_directory = argv[--argc];
|
|
|
|
argv[argc] = NULL;
|
|
|
|
}
|
2013-01-08 09:11:44 -05:00
|
|
|
|
|
|
|
if ( argc < 2 )
|
2016-09-18 18:57:22 -04:00
|
|
|
errx(1, "missing file operand");
|
2013-01-08 09:11:44 -05:00
|
|
|
|
|
|
|
bool success = true;
|
|
|
|
for ( int i = 1; i < argc; i++ )
|
|
|
|
{
|
|
|
|
const char* src = argv[i];
|
2014-06-27 16:46:50 -04:00
|
|
|
const char* dst = target_directory;
|
2016-09-18 18:57:22 -04:00
|
|
|
if ( !cp_directory(AT_FDCWD, src, src,
|
|
|
|
AT_FDCWD, dst, dst,
|
|
|
|
flags, symbolic_dereference) )
|
2013-01-08 09:11:44 -05:00
|
|
|
success = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return success ? 0 : 1;
|
2011-11-21 08:57:02 -05:00
|
|
|
}
|