#include <sys/time.h>
#include <signal.h>
#include <locale.h>
#include <stdarg.h>
#include <sys/acl.h>
#include <libcmdutils.h>
#include <aclutils.h>
#include <assert.h>
#include "getresponse.h"
#define FTYPE(A) (A.st_mode)
#define FMODE(A) (A.st_mode)
#define UID(A) (A.st_uid)
#define GID(A) (A.st_gid)
#define IDENTICAL(A, B) (A.st_dev == B.st_dev && A.st_ino == B.st_ino)
#define ISDIR(A) ((A.st_mode & S_IFMT) == S_IFDIR)
#define ISDOOR(A) ((A.st_mode & S_IFMT) == S_IFDOOR)
#define ISLNK(A) ((A.st_mode & S_IFMT) == S_IFLNK)
#define ISREG(A) (((A).st_mode & S_IFMT) == S_IFREG)
#define ISDEV(A) ((A.st_mode & S_IFMT) == S_IFCHR || \
(A.st_mode & S_IFMT) == S_IFBLK || \
(A.st_mode & S_IFMT) == S_IFIFO)
#define ISSOCK(A) ((A.st_mode & S_IFMT) == S_IFSOCK)
#define DELIM '/'
#define EQ(x, y) (strcmp(x, y) == 0)
#define FALSE 0
#define MODEBITS (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)
#define TRUE 1
typedef enum {
CHK_CONT,
CHK_ERROR,
CHK_SKIP
} chkfiles_t;
static chkfiles_t chkfiles(const char *, char **);
static const char *dname(const char *);
static int lnkfil(const char *, char *);
static int cpymve(const char *, char *);
static int rcopy(const char *, char *);
static int chk_different(const char *, const char *);
static int chg_time(const char *, struct stat);
static int chg_mode(const char *, uid_t, gid_t, mode_t);
static int copydir(const char *, char *);
static int copyspecial(const char *);
static int getrealpath(const char *, char *);
static void usage(void);
static void Perror(const char *);
static void Perror2(const char *, const char *);
static int use_stdin(void);
static int copyattributes(const char *, const char *);
static int copy_sysattr(const char *, const char *);
static tree_node_t *create_tnode(dev_t, ino_t);
static struct stat s1, s2, s3, s4;
static int cpy = FALSE;
static int mve = FALSE;
static int lnk = FALSE;
static char *cmd;
static int fflg = 0;
static int pflg = 0;
static int Rflg = 0;
static int rflg = 0;
static int sflg = 0;
static int Hflg = 0;
static int Lflg = 0;
static int Pflg = 0;
static int atflg = 0;
static int Tflg = 0;
static int attrsilent = 0;
static int targetexists = 0;
static int cmdarg;
static avl_tree_t *stree = NULL;
static acl_t *s1acl;
static int saflg = 0;
static int srcfd = -1;
static int targfd = -1;
static int sourcedirfd = -1;
static int targetdirfd = -1;
static DIR *srcdirp = NULL;
static int srcattrfd = -1;
static int targattrfd = -1;
static struct stat attrdir;
typedef enum {
TA_DEFAULT,
TA_OVERWRITE,
TA_PROMPT,
TA_SKIP
} target_action_t;
target_action_t targact = TA_DEFAULT;
static int open_source(const char *);
static int open_target_srctarg_attrdirs(const char *, const char *);
static int open_attrdirp(const char *);
static int traverse_attrfile(struct dirent *, const char *, const char *, int);
static void rewind_attrdir(DIR *);
static void close_all(void);
int
main(int argc, char *argv[])
{
int c, i, r, errflg = 0;
char target[PATH_MAX];
int (*move)(const char *, char *);
if ((cmd = strrchr(argv[0], '/')) != NULL)
++cmd;
else
cmd = argv[0];
(void) setlocale(LC_ALL, "");
#if !defined(TEXT_DOMAIN)
#define TEXT_DOMAIN "SYS_TEST"
#endif
(void) textdomain(TEXT_DOMAIN);
if (init_yes() < 0) {
(void) fprintf(stderr, gettext(ERR_MSG_INIT_YES),
strerror(errno));
exit(3);
}
if (EQ(cmd, "mv"))
mve = TRUE;
else if (EQ(cmd, "ln"))
lnk = TRUE;
else if (EQ(cmd, "cp"))
cpy = TRUE;
else {
(void) fprintf(stderr,
gettext("Invalid command name (%s); expecting "
"mv, cp, or ln.\n"), cmd);
exit(1);
}
if (cpy) {
while ((c = getopt(argc, argv, "afHinLpPrRT@/")) != EOF)
switch (c) {
case 'f':
fflg++;
break;
case 'i':
targact = TA_PROMPT;
break;
case 'n':
targact = TA_SKIP;
break;
case 'p':
pflg++;
#ifdef XPG4
attrsilent = 1;
atflg = 0;
saflg = 0;
#else
if (atflg == 0)
attrsilent = 1;
#endif
break;
case 'H':
Lflg = Pflg = 0;
Hflg++;
break;
case 'L':
Hflg = Pflg = 0;
Lflg++;
break;
case 'P':
Lflg = Hflg = 0;
Pflg++;
break;
case 'R':
Rflg++;
case 'r':
rflg++;
break;
case 'a':
Lflg = Hflg = 0;
pflg++;
Pflg++;
Rflg++;
rflg++;
break;
case 'T':
Tflg = 1;
break;
case '@':
atflg++;
attrsilent = 0;
#ifdef XPG4
pflg = 0;
#endif
break;
case '/':
saflg++;
attrsilent = 0;
#ifdef XPG4
pflg = 0;
#endif
break;
default:
errflg++;
}
if ((Hflg || Lflg || Pflg) && !(Rflg || rflg)) {
errflg++;
}
} else if (mve) {
while ((c = getopt(argc, argv, "finsT")) != EOF)
switch (c) {
case 'f':
targact = TA_OVERWRITE;
break;
case 'i':
#ifdef XPG4
targact = TA_PROMPT;
#else
if (targact != TA_OVERWRITE) {
targact = TA_PROMPT;
}
#endif
break;
case 'n':
targact = TA_SKIP;
break;
case 'T':
Tflg = 1;
break;
default:
errflg++;
}
} else {
#ifdef XPG4
Lflg = 1;
#endif
while ((c = getopt(argc, argv, "fiLnPsT")) != EOF)
switch (c) {
case 'f':
targact = TA_OVERWRITE;
break;
case 'i':
targact = TA_PROMPT;
break;
case 'L':
Lflg = 1;
Pflg = 0;
break;
case 'n':
break;
case 'P':
Pflg = 1;
Lflg = 0;
break;
case 's':
sflg++;
break;
case 'T':
Tflg = 1;
break;
default:
errflg++;
}
}
if (mve && optind < argc && (strcmp(argv[optind], "-") == 0))
optind++;
argc -= optind;
argv = &argv[optind];
if ((argc < 2 && lnk != TRUE) || (argc < 1 && lnk == TRUE)) {
(void) fprintf(stderr,
gettext("%s: Insufficient arguments (%d)\n"),
cmd, argc);
usage();
}
if (errflg != 0)
usage();
if (argc > 2) {
if (Tflg) {
(void) fprintf(stderr, gettext("%s: only a single "
"source and target can be used with -T\n"), cmd);
exit(2);
}
if (stat(argv[argc-1], &s2) < 0) {
(void) fprintf(stderr,
gettext("%s: %s not found\n"),
cmd, argv[argc-1]);
exit(2);
}
if (!ISDIR(s2)) {
(void) fprintf(stderr,
gettext("%s: Target %s must be a directory\n"),
cmd, argv[argc-1]);
usage();
}
}
if (strlen(argv[argc-1]) >= PATH_MAX) {
(void) fprintf(stderr,
gettext("%s: Target %s file name length exceeds PATH_MAX"
" %d\n"), cmd, argv[argc-1], PATH_MAX);
exit(78);
}
if (argc == 1) {
if (!lnk)
usage();
if (Tflg) {
(void) fprintf(stderr, gettext("%s: -T requires "
"specifying a target argument\n"), cmd);
exit(2);
}
(void) strcpy(target, ".");
} else {
(void) strcpy(target, argv[--argc]);
}
if (lnk)
move = lnkfil;
else
move = cpymve;
r = 0;
for (i = 0; i < argc; i++) {
stree = NULL;
cmdarg = 1;
r += move(argv[i], target);
}
return (r ? 2 : 0);
}
static int
lnkfil(const char *source, char *target)
{
char *buf = NULL;
int flags;
if (sflg) {
if ((stat(target, &s2) >= 0) && ISDIR(s2) && !Tflg) {
size_t len;
len = strlen(target) + strlen(dname(source)) + 4;
if ((buf = (char *)malloc(len)) == NULL) {
(void) fprintf(stderr,
gettext("%s: Insufficient memory "
"to %s %s\n"), cmd, cmd, source);
exit(3);
}
(void) snprintf(buf, len, "%s/%s",
target, dname(source));
target = buf;
}
if ((lstat(target, &s2) == 0)) {
switch (targact) {
case TA_OVERWRITE:
case TA_PROMPT:
if (ISDIR(s2)) {
(void) fprintf(stderr,
gettext("%s: cannot create link "
"over directory %s\n"), cmd,
target);
return (1);
}
if (targact == TA_PROMPT && use_stdin()) {
(void) fprintf(stderr,
gettext("%s: overwrite %s "
"(%s/%s)? "), cmd, target, yesstr,
nostr);
if (yes() == 0) {
return (0);
}
}
if (unlink(target) < 0) {
(void) fprintf(stderr,
gettext("%s: cannot unlink %s: "),
cmd, target);
perror("");
return (1);
}
case TA_DEFAULT:
break;
case TA_SKIP:
abort();
}
}
if (symlink(source, target) < 0) {
(void) fprintf(stderr,
gettext("%s: cannot create %s: "),
cmd, target);
perror("");
if (buf != NULL)
free(buf);
return (1);
}
if (buf != NULL)
free(buf);
return (0);
}
switch (chkfiles(source, &target)) {
case CHK_ERROR:
return (1);
case CHK_SKIP:
return (0);
case CHK_CONT:
break;
}
if (ISDIR(s1)) {
(void) fprintf(stderr,
gettext("%s: %s is a directory\n"), cmd, source);
return (1);
}
flags = Lflg ? AT_SYMLINK_FOLLOW : 0;
if (linkat(AT_FDCWD, source, AT_FDCWD, target, flags) < 0) {
if (errno == EXDEV)
(void) fprintf(stderr,
gettext("%s: %s is on a different file system\n"),
cmd, target);
else {
(void) fprintf(stderr,
gettext("%s: cannot create link %s: "),
cmd, target);
perror("");
}
if (buf != NULL)
free(buf);
return (1);
} else {
if (buf != NULL)
free(buf);
return (0);
}
}
static int
cpymve(const char *source, char *target)
{
int n;
int fi, fo;
int ret = 0;
int attret = 0;
int sattret = 0;
int error = 0;
switch (chkfiles(source, &target)) {
case CHK_ERROR:
return (1);
case CHK_SKIP:
return (0);
case CHK_CONT:
break;
}
if (cpy) {
if (ISDIR(s1)) {
int rc;
avl_index_t where = 0;
tree_node_t *tnode;
tree_node_t *tptr;
dev_t save_dev = s1.st_dev;
ino_t save_ino = s1.st_ino;
if ((rc = add_tnode(&stree, save_dev, save_ino)) != 1) {
if (rc == 0) {
(void) fprintf(stderr, gettext(
"%s: cycle detected: %s\n"),
cmd, source);
} else {
Perror(source);
}
return (1);
}
cmdarg = 0;
rc = copydir(source, target);
if ((tnode = create_tnode(save_dev,
save_ino)) == NULL) {
Perror(source);
return (1);
}
if ((tptr = avl_find(stree, tnode, &where)) != NULL) {
avl_remove(stree, tptr);
}
free(tptr);
free(tnode);
return (rc);
} else if (ISDEV(s1) && Rflg) {
return (copyspecial(target));
} else {
goto copy;
}
}
if (mve) {
if (rename(source, target) >= 0)
return (0);
if (errno != EXDEV) {
if (errno == ENOTDIR && ISDIR(s1)) {
(void) fprintf(stderr,
gettext("%s: %s is a directory\n"),
cmd, source);
return (1);
}
(void) fprintf(stderr,
gettext("%s: cannot rename %s to %s: "),
cmd, source, target);
perror("");
return (1);
}
if (targetexists && ISDIR(s2) && (!ISDIR(s1))) {
(void) fprintf(stderr,
gettext("%s: cannot mv a non directory %s "
"over existing directory"
" %s \n"), cmd, source, target);
return (1);
}
if (ISDIR(s1)) {
#ifdef XPG4
if (targetexists && ISDIR(s2)) {
if (rmdir(target) < 0) {
int errno_save = errno;
(void) fprintf(stderr,
gettext("%s: cannot rmdir %s: "),
cmd, target);
errno = errno_save;
perror("");
return (1);
}
}
#endif
if ((n = copydir(source, target)) == 0)
(void) rmdir(source);
return (n);
}
if (ISDOOR(s1)) {
(void) fprintf(stderr,
gettext("%s: %s: cannot move door "
"across file systems\n"), cmd, source);
return (1);
}
if (ISSOCK(s1)) {
(void) fprintf(stderr,
gettext("%s: %s: cannot move socket "
"across file systems\n"), cmd, source);
return (1);
}
if (ISLNK(s1)) {
register int m;
register mode_t md;
char symln[PATH_MAX + 1];
if (targetexists && unlink(target) < 0) {
(void) fprintf(stderr,
gettext("%s: cannot unlink %s: "),
cmd, target);
perror("");
return (1);
}
if ((m = readlink(source, symln,
sizeof (symln) - 1)) < 0) {
Perror(source);
return (1);
}
symln[m] = '\0';
md = umask(~(s1.st_mode & MODEBITS));
if (symlink(symln, target) < 0) {
Perror(target);
return (1);
}
(void) umask(md);
m = lchown(target, UID(s1), GID(s1));
#ifdef XPG4
if (m < 0) {
(void) fprintf(stderr, gettext("%s: cannot"
" change owner and group of"
" %s: "), cmd, target);
perror("");
}
#endif
goto cleanup;
}
if (ISDEV(s1)) {
if (targetexists && unlink(target) < 0) {
(void) fprintf(stderr,
gettext("%s: cannot unlink %s: "),
cmd, target);
perror("");
return (1);
}
if (mknod(target, s1.st_mode, s1.st_rdev) < 0) {
Perror(target);
return (1);
}
(void) chg_mode(target, UID(s1), GID(s1), FMODE(s1));
(void) chg_time(target, s1);
goto cleanup;
}
if (ISREG(s1)) {
if (ISDIR(s2)) {
if (targetexists && rmdir(target) < 0) {
(void) fprintf(stderr,
gettext("%s: cannot rmdir %s: "),
cmd, target);
perror("");
return (1);
}
} else {
if (targetexists && unlink(target) < 0) {
(void) fprintf(stderr,
gettext("%s: cannot unlink %s: "),
cmd, target);
perror("");
return (1);
}
}
copy:
if (cpy && (Pflg || (Hflg && !cmdarg)) && (ISLNK(s1))) {
int m;
mode_t md;
char symln[PATH_MAX + 1];
m = readlink(source, symln, sizeof (symln) - 1);
if (m < 0) {
Perror(source);
return (1);
}
symln[m] = '\0';
md = umask(~(s1.st_mode & MODEBITS));
if (symlink(symln, target) < 0) {
Perror(target);
return (1);
}
(void) umask(md);
m = lchown(target, UID(s1), GID(s1));
if (m < 0) {
(void) fprintf(stderr, gettext(
"cp: cannot change owner and "
"group of %s:"), target);
perror("");
}
} else {
fi = open(source, O_RDONLY);
if (fi < 0) {
(void) fprintf(stderr,
gettext("%s: cannot open %s: "),
cmd, source);
perror("");
return (1);
}
fo = creat(target, s1.st_mode & MODEBITS);
if (fo < 0) {
if (fflg) {
(void) unlink(target);
fo = creat(target,
s1.st_mode & MODEBITS);
}
}
if (fo < 0) {
(void) fprintf(stderr,
gettext("%s: cannot create %s: "),
cmd, target);
perror("");
(void) close(fi);
return (1);
} else {
(void) stat(target, &s2);
}
if (pflg || mve) {
(void) chmod(target, FMODE(s1));
if (s1acl != NULL) {
if ((acl_set(target,
s1acl)) < 0) {
error++;
(void) fprintf(stderr,
gettext("%s: "
"Failed to set "
"acl entries "
"on %s\n"), cmd,
target);
acl_free(s1acl);
s1acl = NULL;
}
}
}
if (fstat(fi, &s1) < 0) {
(void) fprintf(stderr,
gettext("%s: cannot access %s\n"),
cmd, source);
return (1);
}
if (IDENTICAL(s1, s2)) {
(void) fprintf(stderr,
gettext(
"%s: %s and %s are identical\n"),
cmd, source, target);
return (1);
}
if (writefile(fi, fo, source, target, NULL,
NULL, &s1, &s2) != 0) {
return (1);
}
(void) close(fi);
if (close(fo) < 0) {
Perror2(target, "write");
return (1);
}
}
if (pflg || atflg || mve || saflg) {
attret = copyattributes(source, target);
if (attret != 0 && !attrsilent) {
(void) fprintf(stderr, gettext(
"%s: Failed to preserve"
" extended attributes of file"
" %s\n"), cmd, source);
}
if (pflg || mve || saflg)
sattret = copy_sysattr(source, target);
if (mve && attret != 0) {
(void) unlink(target);
return (1);
}
if (attrsilent) {
attret = 0;
}
}
if (pflg || mve) {
if ((ret = chg_mode(target, UID(s1), GID(s1),
FMODE(s1))) > 0)
return (1);
if (s1acl != NULL) {
if ((acl_set(target, s1acl)) < 0) {
error++;
(void) fprintf(stderr,
gettext("%s: Failed to "
"set acl entries "
"on %s\n"), cmd, target);
}
}
if ((ret = chg_time(target, s1)) > 0)
return (1);
}
if (cpy) {
if (error != 0 || attret != 0 || sattret != 0)
return (1);
return (0);
}
goto cleanup;
}
(void) fprintf(stderr,
gettext("%s: %s: unknown file type 0x%x\n"), cmd,
source, (s1.st_mode & S_IFMT));
return (1);
cleanup:
if (unlink(source) < 0) {
(void) unlink(target);
(void) fprintf(stderr,
gettext("%s: cannot unlink %s: "),
cmd, source);
perror("");
return (1);
}
if (error != 0 || attret != 0 || sattret != 0)
return (1);
return (ret);
}
return (ret);
}
static tree_node_t *
create_tnode(dev_t dev, ino_t ino)
{
tree_node_t *tnode;
if ((tnode = (tree_node_t *)malloc(sizeof (tree_node_t))) != NULL) {
tnode->node_dev = dev;
tnode->node_ino = ino;
}
return (tnode);
}
static chkfiles_t
chkfiles(const char *source, char **to)
{
char *buf = (char *)NULL;
int (*statf)(const char *, struct stat *) = lstat;
char *target = *to;
int error;
if (cpy && !(Pflg || (Hflg && !cmdarg))) {
statf = stat;
} else if (lnk && !sflg && Lflg) {
statf = stat;
}
if ((*statf)(source, &s1) < 0) {
if (errno == ENOTDIR)
(void) fprintf(stderr, "%s: %s: %s\n", cmd, source,
strerror(errno));
else
(void) fprintf(stderr,
gettext("%s: cannot access %s\n"), cmd, source);
return (CHK_ERROR);
}
if (!lnk && !ISLNK(s1)) {
if (s1acl != NULL) {
acl_free(s1acl);
s1acl = NULL;
}
if ((error = acl_get(source, ACL_NO_TRIVIAL, &s1acl)) != 0) {
(void) fprintf(stderr,
"%s: failed to get acl entries: %s\n", source,
acl_strerror(error));
return (CHK_ERROR);
}
}
FTYPE(s2) = S_IFREG;
targetexists = 0;
if ((*statf)(target, &s2) >= 0) {
if (ISLNK(s2))
(void) stat(target, &s2);
if (ISDIR(s2) && !Tflg) {
size_t len;
len = strlen(target) + strlen(dname(source)) + 4;
if ((buf = (char *)malloc(len)) == NULL) {
(void) fprintf(stderr,
gettext("%s: Insufficient memory to "
"%s %s\n "), cmd, cmd, source);
exit(3);
}
(void) snprintf(buf, len, "%s/%s",
target, dname(source));
*to = target = buf;
}
if ((*statf)(target, &s2) >= 0) {
boolean_t prompt = B_FALSE;
boolean_t overwrite = B_FALSE;
boolean_t override = B_FALSE;
targetexists++;
if (cpy || mve) {
if (IDENTICAL(s1, s2)) {
(void) fprintf(stderr,
gettext(
"%s: %s and %s are identical\n"),
cmd, source, target);
if (buf != NULL)
free(buf);
return (CHK_ERROR);
}
}
if (lnk) {
if (!chk_different(source, target)) {
if (buf != NULL)
free(buf);
return (CHK_ERROR);
}
}
switch (targact) {
case TA_SKIP:
if (buf != NULL)
free(buf);
return (CHK_SKIP);
case TA_OVERWRITE:
break;
case TA_PROMPT:
if (use_stdin()) {
prompt = B_TRUE;
overwrite = B_TRUE;
if (mve && access(target, W_OK) < 0 &&
!ISLNK(s2)) {
override = B_TRUE;
}
}
break;
case TA_DEFAULT:
if (lnk) {
(void) fprintf(stderr,
gettext("%s: %s: File exists\n"),
cmd, target);
if (buf != NULL)
free(buf);
return (CHK_ERROR);
}
if (mve && access(target, W_OK) < 0 &&
use_stdin() && !ISLNK(s2)) {
prompt = B_TRUE;
override = B_TRUE;
}
break;
}
if (prompt) {
assert(overwrite || override);
if (overwrite && override) {
(void) fprintf(stderr, gettext("%s: "
"overwrite %s and override "
"protection %o (%s/%s)? "), cmd,
target, FMODE(s2) & MODEBITS,
yesstr, nostr);
} else if (overwrite) {
(void) fprintf(stderr, gettext("%s: "
"overwrite %s (%s/%s)? "), cmd,
target, yesstr, nostr);
} else if (override) {
(void) fprintf(stderr, gettext("%s: "
"%s: override protection %o "
"(%s/%s)? "), cmd, target,
FMODE(s2) & MODEBITS, yesstr,
nostr);
}
if (yes() == 0) {
if (buf != NULL)
free(buf);
return (CHK_SKIP);
}
}
if (lnk && unlink(target) < 0) {
(void) fprintf(stderr,
gettext("%s: cannot unlink %s: "),
cmd, target);
perror("");
return (CHK_ERROR);
}
}
}
return (CHK_CONT);
}
static int
chk_different(const char *source, const char *target)
{
char rtarget[PATH_MAX], rsource[PATH_MAX];
if (IDENTICAL(s1, s2)) {
if ((getrealpath(source, rsource) == 0) ||
(getrealpath(target, rtarget) == 0)) {
return (0);
}
if (strncmp(rsource, rtarget, PATH_MAX) == 0) {
(void) fprintf(stderr, gettext(
"%s: %s and %s are identical\n"),
cmd, source, target);
return (0);
}
}
return (1);
}
static int
getrealpath(const char *path, char *rpath)
{
if (realpath(path, rpath) == NULL) {
int errno_save = errno;
(void) fprintf(stderr, gettext(
"%s: cannot resolve path %s: "), cmd, path);
errno = errno_save;
perror("");
return (0);
}
return (1);
}
static int
rcopy(const char *from, char *to)
{
DIR *fold = opendir(from);
struct dirent *dp;
struct stat statb, s1save;
int errs = 0;
char fromname[PATH_MAX];
if (fold == 0 || ((pflg || mve) && fstat(fold->dd_fd, &statb) < 0)) {
Perror(from);
return (1);
}
if (pflg || mve) {
s1save = s1;
}
for (;;) {
dp = readdir(fold);
if (dp == 0) {
(void) closedir(fold);
if (pflg || mve)
return (chg_time(to, s1save) + errs);
return (errs);
}
if (dp->d_ino == 0)
continue;
if ((strcmp(dp->d_name, ".") == 0) ||
(strcmp(dp->d_name, "..") == 0))
continue;
if (strlen(from)+1+strlen(dp->d_name) >=
sizeof (fromname) - 1) {
(void) fprintf(stderr,
gettext("%s : %s/%s: Name too long\n"),
cmd, from, dp->d_name);
errs++;
continue;
}
(void) snprintf(fromname, sizeof (fromname),
"%s/%s", from, dp->d_name);
errs += cpymve(fromname, to);
}
}
static const char *
dname(const char *name)
{
const char *p;
p = name;
while (*p)
if (*p++ == DELIM && *p)
name = p;
return (name);
}
static void
usage(void)
{
if (mve) {
(void) fprintf(stderr, gettext(
"Usage: mv [-fin] source target_file\n"
" mv [-fin] source... target_dir\n"
" mv [-fin] -T source target\n"));
} else if (lnk) {
#ifdef XPG4
(void) fprintf(stderr, gettext(
"Usage: ln [-fis] [-L|-P] source_file [target]\n"
" ln [-fis] [-L|-P] source_file... target\n"
" ln [-fis] [-L|-P] -T source_file target\n"));
#else
(void) fprintf(stderr, gettext(
"Usage: ln [-fins] [-L|-P] source_file [target]\n"
" ln [-fins] [-L|-P] source_file... target\n"
" ln [-fins] [-L|-P] -T source_file target\n"));
#endif
} else if (cpy) {
(void) fprintf(stderr, gettext(
"Usage: cp [-afinpT@/] source_file target_file\n"
" cp [-afinp@/] source_file... target\n"
" cp [-afinp@/] -T source_file target\n"
" cp [-r|-R [-H|-L|-P]] [-afinp@/] "
"source_dir... target\n"));
}
exit(2);
}
static int
chg_time(const char *to, struct stat ss)
{
struct timespec times[2];
#ifdef XPG4
int rc;
#endif
times[0] = ss.st_atim;
times[1] = ss.st_mtim;
#ifdef XPG4
rc = utimensat(AT_FDCWD, to, times,
ISLNK(s1) ? AT_SYMLINK_NOFOLLOW : 0);
if ((pflg || mve) && rc != 0) {
(void) fprintf(stderr,
gettext("%s: cannot set times for %s: "), cmd, to);
perror("");
if (pflg)
return (1);
}
#else
(void) utimensat(AT_FDCWD, to, times,
ISLNK(s1) ? AT_SYMLINK_NOFOLLOW : 0);
#endif
return (0);
}
static int
chg_mode(const char *target, uid_t uid, gid_t gid, mode_t mode)
{
int clearflg = 0;
struct stat st;
if (lstat(target, &st) == 0 && ISLNK(st))
return (0);
if (chown(target, uid, gid) != 0) {
#ifdef XPG4
if (mve) {
(void) fprintf(stderr, gettext("%s: cannot change"
" owner and group of %s: "), cmd, target);
perror("");
}
#endif
if (mode & (S_ISUID | S_ISGID)) {
mode &= ~S_ISUID & ~S_ISGID;
++clearflg;
}
}
if (chmod(target, mode) != 0) {
if (clearflg) {
(void) fprintf(stderr, gettext(
"%s: cannot clear S_ISUID and S_ISGID bits in"
" %s: "), cmd, target);
perror("");
if (pflg)
return (1);
}
#ifdef XPG4
else {
(void) fprintf(stderr, gettext(
"%s: cannot set permissions for %s: "), cmd, target);
perror("");
if (pflg)
return (1);
}
#endif
}
return (0);
}
static void
Perror(const char *s)
{
char buf[PATH_MAX + 10];
(void) snprintf(buf, sizeof (buf), "%s: %s", cmd, s);
perror(buf);
}
static void
Perror2(const char *s1, const char *s2)
{
char buf[PATH_MAX + 20];
(void) snprintf(buf, sizeof (buf), "%s: %s: %s",
cmd, gettext(s1), gettext(s2));
perror(buf);
}
static int
copydir(const char *source, char *target)
{
int ret, attret = 0;
int sattret = 0;
int pret = 0;
mode_t fixmode = (mode_t)0;
struct stat s1save;
acl_t *s1acl_save;
int error = 0;
s1acl_save = NULL;
if (cpy && !rflg) {
(void) fprintf(stderr,
gettext("%s: %s: is a directory\n"), cmd, source);
return (1);
}
if (stat(target, &s2) < 0) {
if (mkdir(target, (s1.st_mode & MODEBITS)) < 0) {
(void) fprintf(stderr, "%s: ", cmd);
perror(target);
return (1);
}
if (stat(target, &s2) == 0) {
fixmode = s2.st_mode;
} else {
fixmode = s1.st_mode;
}
(void) chmod(target, ((fixmode & MODEBITS) | S_IRWXU));
} else if (!(ISDIR(s2))) {
(void) fprintf(stderr,
gettext("%s: %s: not a directory.\n"), cmd, target);
return (1);
}
if (pflg || mve) {
s1save = s1;
if (s1acl != NULL) {
s1acl_save = acl_dup(s1acl);
if (s1acl_save == NULL) {
(void) fprintf(stderr, gettext("%s: "
"Insufficient memory to save acl"
" entry\n"), cmd);
if (pflg)
return (1);
}
#ifdef XPG4
else {
(void) fprintf(stderr, gettext("%s: "
"Insufficient memory to save acl"
" entry\n"), cmd);
if (pflg)
return (1);
}
#endif
}
}
ret = rcopy(source, target);
if (pflg || mve) {
if ((pret = chg_mode(target, UID(s1save), GID(s1save),
FMODE(s1save))) == 0)
pret = chg_time(target, s1save);
ret += pret;
if (s1acl_save != NULL) {
if (acl_set(target, s1acl_save) < 0) {
error++;
#ifdef XPG4
if (pflg || mve) {
#else
if (pflg) {
#endif
(void) fprintf(stderr, gettext(
"%s: failed to set acl entries "
"on %s\n"), cmd, target);
if (pflg) {
acl_free(s1acl_save);
s1acl_save = NULL;
ret++;
}
}
}
acl_free(s1acl_save);
s1acl_save = NULL;
}
} else if (fixmode != (mode_t)0)
(void) chmod(target, fixmode & MODEBITS);
if (pflg || atflg || mve || saflg) {
attret = copyattributes(source, target);
if (!attrsilent && attret != 0) {
(void) fprintf(stderr, gettext("%s: Failed to preserve"
" extended attributes of directory"
" %s\n"), cmd, source);
} else {
attret = 0;
}
if (pflg || mve || saflg) {
sattret = copy_sysattr(source, target);
if (sattret != 0) {
(void) fprintf(stderr, gettext(
"%s: Failed to preserve "
"extended system attributes "
"of directory %s\n"), cmd, source);
}
}
}
if (attret != 0 || sattret != 0 || error != 0)
return (1);
return (ret);
}
static int
copyspecial(const char *target)
{
int ret = 0;
if (mknod(target, s1.st_mode, s1.st_rdev) != 0) {
(void) fprintf(stderr, gettext(
"cp: cannot create special file %s: "), target);
perror("");
return (1);
}
if (pflg) {
if ((ret = chg_mode(target, UID(s1), GID(s1), FMODE(s1))) == 0)
ret = chg_time(target, s1);
}
return (ret);
}
static int
use_stdin(void)
{
#ifdef XPG4
return (1);
#else
return (isatty(fileno(stdin)));
#endif
}
static int
copyattributes(const char *source, const char *target)
{
struct dirent *dp;
int error = 0;
int aclerror;
mode_t mode;
int clearflg = 0;
acl_t *xacl = NULL;
acl_t *attrdiracl = NULL;
struct timespec times[2];
if (pathconf(source, _PC_XATTR_EXISTS) != 1)
return (0);
if (pathconf(target, _PC_XATTR_ENABLED) != 1) {
if (!attrsilent) {
(void) fprintf(stderr,
gettext(
"%s: cannot preserve extended attributes, "
"operation not supported on file"
" %s\n"), cmd, target);
}
return (1);
}
if (open_source(source) != 0)
return (1);
if (open_target_srctarg_attrdirs(source, target) != 0)
return (1);
if (open_attrdirp(source) != 0)
return (1);
if (pflg || mve) {
if (fchmod(targetdirfd, attrdir.st_mode) == -1) {
if (!attrsilent) {
(void) fprintf(stderr,
gettext("%s: failed to set file mode"
" correctly on attribute directory of"
" file %s: "), cmd, target);
perror("");
++error;
}
}
if (fchown(targetdirfd, attrdir.st_uid, attrdir.st_gid) == -1) {
if (!attrsilent) {
(void) fprintf(stderr,
gettext("%s: failed to set file"
" ownership correctly on attribute"
" directory of file %s: "), cmd, target);
perror("");
++error;
}
}
times[0] = attrdir.st_atim;
times[1] = attrdir.st_mtim;
if (utimensat(targetdirfd, ".", times, 0) < 0) {
if (!attrsilent) {
(void) fprintf(stderr,
gettext("%s: cannot set attribute times"
" for %s: "), cmd, target);
perror("");
++error;
}
}
if ((aclerror = facl_get(sourcedirfd,
ACL_NO_TRIVIAL, &attrdiracl)) != 0) {
if (!attrsilent) {
(void) fprintf(stderr, gettext(
"%s: failed to get acl entries of"
" attribute directory for"
" %s : %s\n"), cmd,
source, acl_strerror(aclerror));
++error;
}
}
if (attrdiracl) {
if (facl_set(targetdirfd, attrdiracl) != 0) {
if (!attrsilent) {
(void) fprintf(stderr, gettext(
"%s: failed to set acl entries"
" on attribute directory "
"for %s\n"), cmd, target);
++error;
}
acl_free(attrdiracl);
attrdiracl = NULL;
}
}
}
while ((dp = readdir(srcdirp)) != NULL) {
int ret;
if ((ret = traverse_attrfile(dp, source, target, 1)) == -1)
continue;
else if (ret > 0) {
++error;
goto out;
}
if (pflg || mve) {
if ((aclerror = facl_get(srcattrfd,
ACL_NO_TRIVIAL, &xacl)) != 0) {
if (!attrsilent) {
(void) fprintf(stderr, gettext(
"%s: failed to get acl entries of"
" attribute %s for"
" %s: %s"), cmd, dp->d_name,
source, acl_strerror(aclerror));
++error;
}
}
}
if ((pflg || mve) && xacl != NULL) {
if ((facl_set(targattrfd, xacl)) < 0) {
if (!attrsilent) {
(void) fprintf(stderr, gettext(
"%s: failed to set acl entries on"
" attribute %s for"
"%s\n"), cmd, dp->d_name, target);
++error;
}
acl_free(xacl);
xacl = NULL;
}
}
if (writefile(srcattrfd, targattrfd, source, target,
dp->d_name, dp->d_name, &s3, &s4) != 0) {
if (!attrsilent) {
++error;
}
goto next;
}
if (pflg || mve) {
mode = FMODE(s3);
if (fchown(targattrfd, UID(s3), GID(s3)) != 0) {
if (!attrsilent) {
(void) fprintf(stderr,
gettext("%s: cannot change"
" owner and group of"
" attribute %s for" " file"
" %s: "), cmd, dp->d_name, target);
perror("");
++error;
}
if (mode & (S_ISUID | S_ISGID)) {
mode &= ~S_ISUID & ~S_ISGID;
++clearflg;
}
}
times[0] = s3.st_atim;
times[1] = s3.st_mtim;
if (utimensat(targetdirfd, dp->d_name, times, 0) < 0) {
if (!attrsilent) {
(void) fprintf(stderr,
gettext("%s: cannot set attribute"
" times for %s: "), cmd, target);
perror("");
++error;
}
}
if (fchmod(targattrfd, mode) != 0) {
if (clearflg) {
(void) fprintf(stderr, gettext(
"%s: cannot clear S_ISUID and "
"S_ISGID bits in attribute %s"
" for file"
" %s: "), cmd, dp->d_name, target);
} else {
if (!attrsilent) {
(void) fprintf(stderr,
gettext(
"%s: cannot set permissions of attribute"
" %s for %s: "), cmd, dp->d_name, target);
perror("");
++error;
}
}
}
if (xacl && ((facl_set(targattrfd, xacl)) < 0)) {
if (!attrsilent) {
(void) fprintf(stderr, gettext(
"%s: failed to set acl entries on"
" attribute %s for"
"%s\n"), cmd, dp->d_name, target);
++error;
}
acl_free(xacl);
xacl = NULL;
}
}
next:
if (xacl != NULL) {
acl_free(xacl);
xacl = NULL;
}
if (srcattrfd != -1)
(void) close(srcattrfd);
if (targattrfd != -1)
(void) close(targattrfd);
srcattrfd = targattrfd = -1;
}
out:
if (xacl != NULL) {
acl_free(xacl);
xacl = NULL;
}
if (attrdiracl != NULL) {
acl_free(attrdiracl);
attrdiracl = NULL;
}
if (!saflg && !pflg && !mve)
close_all();
return (error == 0 ? 0 : 1);
}
static int
copy_sysattr(const char *source, const char *target)
{
struct dirent *dp;
nvlist_t *response;
int error = 0;
int target_sa_support = 0;
if (sysattr_support(source, _PC_SATTR_EXISTS) != 1)
return (0);
if (open_source(source) != 0)
return (1);
response = sysattr_list(cmd, srcfd, source);
if (sysattr_support(target, _PC_SATTR_ENABLED) != 1) {
if (response != NULL) {
(void) fprintf(stderr,
gettext(
"%s: cannot preserve extended system "
"attribute, operation not supported on file"
" %s\n"), cmd, target);
error++;
goto out;
}
} else {
target_sa_support = 1;
}
if (target_sa_support) {
if (srcdirp == NULL) {
if (open_target_srctarg_attrdirs(source,
target) != 0) {
error++;
goto out;
}
if (open_attrdirp(source) != 0) {
error++;
goto out;
}
} else {
rewind_attrdir(srcdirp);
}
while ((dp = readdir(srcdirp)) != NULL) {
nvlist_t *res;
int ret;
if ((ret = traverse_attrfile(dp, source, target,
0)) == -1)
continue;
else if (ret > 0) {
++error;
goto out;
}
res = sysattr_list(cmd, srcattrfd, dp->d_name);
if (res == NULL)
goto next;
if (fsetattr(targattrfd,
XATTR_VIEW_READWRITE, res) != 0) {
++error;
(void) fprintf(stderr, gettext("%s: "
"Failed to copy extended system "
"attributes from attribute file "
"%s of %s to %s\n"), cmd,
dp->d_name, source, target);
}
next:
if (srcattrfd != -1)
(void) close(srcattrfd);
if (targattrfd != -1)
(void) close(targattrfd);
srcattrfd = targattrfd = -1;
nvlist_free(res);
}
}
if (target_sa_support && (response != NULL) &&
(fsetattr(targfd, XATTR_VIEW_READWRITE, response)) != 0) {
++error;
(void) fprintf(stderr, gettext("%s: Failed to "
"copy extended system attributes from "
"%s to %s\n"), cmd, source, target);
}
out:
nvlist_free(response);
close_all();
return (error == 0 ? 0 : 1);
}
static int
open_source(const char *src)
{
int error = 0;
srcfd = -1;
if ((srcfd = open(src, O_RDONLY)) == -1) {
if (pflg && attrsilent) {
error++;
goto out;
}
if (!attrsilent) {
(void) fprintf(stderr,
gettext("%s: cannot open file"
" %s: "), cmd, src);
perror("");
}
++error;
}
out:
if (error)
close_all();
return (error == 0 ? 0 : 1);
}
static int
open_target_srctarg_attrdirs(const char *src, const char *targ)
{
int error = 0;
targfd = sourcedirfd = targetdirfd = -1;
if ((targfd = open(targ, O_RDONLY)) == -1) {
if (pflg && attrsilent) {
error++;
goto out;
}
if (!attrsilent) {
(void) fprintf(stderr,
gettext("%s: cannot open file"
" %s: "), cmd, targ);
perror("");
}
++error;
goto out;
}
if ((sourcedirfd = openat(srcfd, ".", O_RDONLY|O_XATTR)) == -1) {
if (pflg && attrsilent) {
error++;
goto out;
}
if (!attrsilent) {
(void) fprintf(stderr,
gettext("%s: cannot open attribute"
" directory for %s: "), cmd, src);
perror("");
}
++error;
goto out;
}
if (fstat(sourcedirfd, &attrdir) == -1) {
if (pflg && attrsilent) {
error++;
goto out;
}
if (!attrsilent) {
(void) fprintf(stderr,
gettext("%s: could not retrieve stat"
" information for attribute directory"
"of file %s: "), cmd, src);
perror("");
}
++error;
goto out;
}
if ((targetdirfd = openat(targfd, ".", O_RDONLY|O_XATTR)) == -1) {
if (pflg && attrsilent) {
error++;
goto out;
}
if (!attrsilent) {
(void) fprintf(stderr,
gettext("%s: cannot open attribute"
" directory for %s: "), cmd, targ);
perror("");
}
++error;
}
out:
if (error)
close_all();
return (error == 0 ? 0 : 1);
}
static int
open_attrdirp(const char *source)
{
int tmpfd = -1;
int error = 0;
if ((tmpfd = dup(sourcedirfd)) == -1) {
if (pflg && attrsilent) {
error++;
goto out;
}
if (!attrsilent) {
(void) fprintf(stderr,
gettext(
"%s: unable to dup attribute directory"
" file descriptor for %s: "), cmd, source);
perror("");
++error;
}
goto out;
}
if ((srcdirp = fdopendir(tmpfd)) == NULL) {
if (pflg && attrsilent) {
error++;
goto out;
}
if (!attrsilent) {
(void) fprintf(stderr,
gettext("%s: failed to open attribute"
" directory for %s: "), cmd, source);
perror("");
++error;
}
}
out:
if (error)
close_all();
return (error == 0 ? 0 : 1);
}
static int
traverse_attrfile(struct dirent *dp, const char *source, const char *target,
int first)
{
int error = 0;
srcattrfd = targattrfd = -1;
if ((dp->d_name[0] == '.' && dp->d_name[1] == '\0') ||
(dp->d_name[0] == '.' && dp->d_name[1] == '.' &&
dp->d_name[2] == '\0') ||
(sysattr_type(dp->d_name) == _RO_SATTR) ||
(sysattr_type(dp->d_name) == _RW_SATTR))
return (-1);
if ((srcattrfd = openat(sourcedirfd, dp->d_name,
O_RDONLY)) == -1) {
if (!attrsilent) {
(void) fprintf(stderr,
gettext("%s: cannot open attribute %s on"
" file %s: "), cmd, dp->d_name, source);
perror("");
++error;
goto out;
}
}
if (fstat(srcattrfd, &s3) < 0) {
if (!attrsilent) {
(void) fprintf(stderr,
gettext("%s: could not stat attribute"
" %s on file"
" %s: "), cmd, dp->d_name, source);
perror("");
++error;
}
goto out;
}
if (first) {
(void) unlinkat(targetdirfd, dp->d_name, 0);
if ((targattrfd = openat(targetdirfd, dp->d_name,
O_RDWR|O_CREAT|O_TRUNC, s3.st_mode & MODEBITS)) == -1) {
if (!attrsilent) {
(void) fprintf(stderr,
gettext("%s: could not create attribute"
" %s on file %s: "), cmd, dp->d_name,
target);
perror("");
++error;
}
goto out;
}
} else {
if ((targattrfd = openat(targetdirfd, dp->d_name,
O_RDONLY)) == -1) {
if (!attrsilent) {
(void) fprintf(stderr,
gettext("%s: could not open attribute"
" %s on file %s: "), cmd, dp->d_name,
target);
perror("");
++error;
}
goto out;
}
}
if (fstat(targattrfd, &s4) < 0) {
if (!attrsilent) {
(void) fprintf(stderr,
gettext("%s: could not stat attribute"
" %s on file"
" %s: "), cmd, dp->d_name, target);
perror("");
++error;
}
}
out:
if (error) {
if (srcattrfd != -1)
(void) close(srcattrfd);
if (targattrfd != -1)
(void) close(targattrfd);
srcattrfd = targattrfd = -1;
}
return (error == 0 ? 0 :1);
}
static void
rewind_attrdir(DIR * sdp)
{
int pwdfd;
pwdfd = open(".", O_RDONLY);
if ((pwdfd != -1) && (fchdir(sourcedirfd) == 0)) {
rewinddir(sdp);
(void) fchdir(pwdfd);
(void) close(pwdfd);
} else {
if (!attrsilent) {
(void) fprintf(stderr, gettext("%s: "
"failed to rewind attribute dir\n"),
cmd);
}
}
}
static void
close_all(void)
{
if (srcattrfd != -1)
(void) close(srcattrfd);
if (targattrfd != -1)
(void) close(targattrfd);
if (sourcedirfd != -1)
(void) close(sourcedirfd);
if (targetdirfd != -1)
(void) close(targetdirfd);
if (srcdirp != NULL) {
(void) closedir(srcdirp);
srcdirp = NULL;
}
if (srcfd != -1)
(void) close(srcfd);
if (targfd != -1)
(void) close(targfd);
}