1976 lines
45 KiB
C
1976 lines
45 KiB
C
/* Copyright (c) 1984 AT&T */ /* All Rights Reserved */
|
|
|
|
/* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T */
|
|
/* The copyright notice above does not evidence any */
|
|
/* actual or intended publication of such source code. */
|
|
|
|
/*
|
|
* Copyright 1989 Silicon Graphics, Inc. All rights reserved.
|
|
*
|
|
* frank@ceres.esd.sgi.com
|
|
* Internationalization done on Oct 27 1992
|
|
*/
|
|
#ident "$Revision: 1.68 $"
|
|
|
|
/*
|
|
* Combined mv/cp/ln command:
|
|
* mv file1 file2
|
|
* mv dir1 dir2
|
|
* mv file1 ... filen dir1
|
|
*/
|
|
#include <errno.h>
|
|
#include <limits.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <fcntl.h>
|
|
#include <locale.h>
|
|
#include <fmtmsg.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <dirent.h>
|
|
#include <sys/param.h>
|
|
#include <sys/stat.h>
|
|
#include <sgi_nl.h>
|
|
#include <msgs/uxsgicore.h>
|
|
#include <ftw.h>
|
|
#include <signal.h>
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/param.h>
|
|
#include <sys/attributes.h>
|
|
#include <sys/statfs.h>
|
|
#include <sys/syssgi.h>
|
|
#include <sys/fstyp.h>
|
|
#include <sys/fsid.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/uuid.h>
|
|
#include <sys/fs/xfs_fsops.h>
|
|
#include <sys/fs/xfs_itable.h>
|
|
|
|
|
|
#define FTYPE(A) (A.st_mode)
|
|
#define FMODE(A) (A.st_mode)
|
|
#define FUSER(A) (A.st_uid)
|
|
#define FGROUP(A) (A.st_gid)
|
|
#define IDENTICAL(A,B) (A.st_dev==B.st_dev && A.st_ino==B.st_ino)
|
|
#define ISBLK(A) ((A.st_mode & S_IFMT) == S_IFBLK)
|
|
#define ISCHR(A) ((A.st_mode & S_IFMT) == S_IFCHR)
|
|
#define ISDIR(A) ((A.st_mode & S_IFMT) == S_IFDIR)
|
|
#define ISFIFO(A) ((A.st_mode & S_IFMT) == S_IFIFO)
|
|
#define ISREG(A) (((A).st_mode & S_IFMT) == S_IFREG)
|
|
#define ISLNK(A) (((A).st_mode & S_IFMT) == S_IFLNK)
|
|
|
|
#define EQ(x,y) !strcmp(x,y)
|
|
#define MODEBITS (~S_IFMT)
|
|
|
|
#define BUFFER_SIZE (1<<16)
|
|
|
|
#define BUFSIZE 1024
|
|
#define min(x, y) ((x) < (y) ? (x) : (y))
|
|
|
|
int fstyp_xfs;
|
|
struct getbmap *outmap = NULL;
|
|
int outmap_size = 0;
|
|
|
|
#define FS_XFS 1
|
|
#define FS_HUH 0
|
|
|
|
struct dioattr from_dio,
|
|
to_dio;
|
|
int argv_blksz_dio=0; /* Default is MAX Block size */
|
|
int do_dio=0; /* Default = Don't do DIRECT_IO */
|
|
int verbose=0;
|
|
int mmap_in = 0;
|
|
int pagesize;
|
|
int cpy_attribs = 0;
|
|
int cpy_holes = 1;
|
|
int try_prealloc = 0;
|
|
int do_rtime = 0;
|
|
int rtextsize = 0;
|
|
int rt_size = -1;
|
|
int do_pad = 0;
|
|
|
|
char *basename();
|
|
void xdev_quit();
|
|
int xdev_state = 0;
|
|
|
|
char *cmd;
|
|
int cpy;
|
|
int mve;
|
|
int lnk;
|
|
char symlnk;
|
|
char iflag;
|
|
char rflag;
|
|
char Rflag;
|
|
char fflag;
|
|
char pflag;
|
|
int silent;
|
|
|
|
/* cmd strings */
|
|
|
|
static char cmd_mv[] = "mv";
|
|
static char cmd_ln[] = "ln";
|
|
static char cmd_cp[] = "cp";
|
|
|
|
static char cmd_label[16] = "UX:??";
|
|
|
|
static char xmsgbuf[PATH_MAX+80]; /* buffer for message */
|
|
|
|
/* some often used strings */
|
|
|
|
static char x_directory[] = "directory";
|
|
static char x_file[] = "file";
|
|
|
|
/*
|
|
* compose message string
|
|
*/
|
|
struct xxtext {
|
|
char *xxcat_txt; /* %s or message */
|
|
char *xxdef_txt; /* default message */
|
|
};
|
|
|
|
int read_fd_bmap(int, int, struct stat64 *, struct stat64 *);
|
|
int nftw_cp(const char *, const struct stat64 *, int flag, struct FTW *);
|
|
int nftw_rm(const char *, const struct stat64 *, int flag, struct FTW *);
|
|
char xdev_target[MAXPATHLEN+1];
|
|
char xdev_source[MAXPATHLEN+1];
|
|
|
|
char *compmsg(xtp)
|
|
struct xxtext *xtp;
|
|
{
|
|
for(xmsgbuf[0] = 0; xtp->xxcat_txt; xtp++) {
|
|
strcat(xmsgbuf, (xtp->xxdef_txt)?
|
|
gettxt(xtp->xxcat_txt, xtp->xxdef_txt) : xtp->xxcat_txt);
|
|
strcat(xmsgbuf, " ");
|
|
}
|
|
return(xmsgbuf);
|
|
}
|
|
|
|
/*
|
|
* Display usage message.
|
|
*
|
|
* For cp the options mean:
|
|
* -a copy user attributes
|
|
* -M memory map input instead of doing read *** disabled ***
|
|
* -D use direct I/O
|
|
* -A preallocate space for output file *** disabled ***
|
|
* -t create output file in realtime partition
|
|
* -b blk specify I/O block size to use
|
|
* -e extsize realtime extent size requested
|
|
* -P pad realtime output to extent boundary
|
|
*/
|
|
static char opt_cp[] = "cp [-aDfirRp] [-b size] [-tP -[e size]]";
|
|
static char opt_mv[] = "mv [-fi]";
|
|
static char opt_ln[] = "ln [-fis]";
|
|
static char xx_MMX_usage[] = _SGI_DMMX_usagespc;
|
|
static char xx_usage[] = " ";
|
|
|
|
static struct xxtext xxx_txt[] = {
|
|
{ _SGI_DMMX_Usage, "Usage:" },
|
|
{ opt_mv, 0 },
|
|
{ _SGI_DMMX_file1, "f1" },
|
|
{ _SGI_DMMX_file2, "f2" },
|
|
{ 0, 0, }
|
|
};
|
|
static struct xxtext xxx_txt2[] = {
|
|
{ xx_MMX_usage, xx_usage },
|
|
{ opt_mv, 0 },
|
|
{ _SGI_DMMX_file1toN, "f1 ... f2" },
|
|
{ _SGI_DMMX_dir, "dir" },
|
|
{ 0, 0, }
|
|
};
|
|
static struct xxtext mv_txt3[] = {
|
|
{ xx_MMX_usage, xx_usage },
|
|
{ opt_mv, 0 },
|
|
{ _SGI_DMMX_dir1, "dir1" },
|
|
{ _SGI_DMMX_dir2, "dir2" },
|
|
{ 0, 0 }
|
|
};
|
|
|
|
void
|
|
usage(exitflg)
|
|
{
|
|
if(cpy)
|
|
xxx_txt[1].xxcat_txt = xxx_txt2[1].xxcat_txt = opt_cp;
|
|
if(lnk)
|
|
xxx_txt[1].xxcat_txt = xxx_txt2[1].xxcat_txt = opt_ln;
|
|
_sgi_ffmtmsg(stderr, 0, cmd_label, MM_INFO, compmsg(xxx_txt));
|
|
_sgi_ffmtmsg(stderr, 0, cmd_label, MM_INFO, compmsg(xxx_txt2));
|
|
if(mve)
|
|
_sgi_ffmtmsg(stderr, 0, cmd_label, MM_INFO, compmsg(mv_txt3));
|
|
if(exitflg)
|
|
exit(2);
|
|
}
|
|
|
|
void
|
|
Error1(s)
|
|
char *s;
|
|
{
|
|
_sgi_nl_error(SGINL_SYSERR2, cmd_label, "%s", s);
|
|
}
|
|
|
|
void
|
|
CannotOpen(f)
|
|
char *f;
|
|
{
|
|
_sgi_nl_error(SGINL_SYSERR2, cmd_label,
|
|
gettxt(_SGI_DMMX_CannotOpen, "Cannot open %s"), f);
|
|
}
|
|
|
|
void
|
|
BadWrite(f)
|
|
char *f;
|
|
{
|
|
_sgi_nl_error(SGINL_SYSERR2, cmd_label,
|
|
gettxt(_SGI_DMMX_CannotWrite, "bad write to %s"), f);
|
|
}
|
|
|
|
/*
|
|
* main entry
|
|
*/
|
|
main(argc, argv)
|
|
register char *argv[];
|
|
{
|
|
register int c, i, r, errflg = 0;
|
|
struct stat64 s1, s2;
|
|
extern char *optarg;
|
|
extern int optind;
|
|
|
|
/*
|
|
* intnl support
|
|
*/
|
|
(void)setlocale(LC_ALL, "");
|
|
(void)setcat("uxsgicore");
|
|
(void)setlabel(cmd_label); /* will be changed later */
|
|
|
|
/*
|
|
* Determine command invoked (mv, cp, or ln)
|
|
*/
|
|
if (cmd = strrchr(argv[0], '/'))
|
|
++cmd;
|
|
else
|
|
cmd = argv[0];
|
|
|
|
/*
|
|
* Set flags based on command.
|
|
*/
|
|
if (EQ(cmd, cmd_mv))
|
|
mve = 1;
|
|
else if (EQ(cmd, cmd_ln))
|
|
lnk = 1;
|
|
else if (EQ(cmd, cmd_cp))
|
|
cpy = 1;
|
|
else {
|
|
_sgi_ffmtmsg(stderr, 0, cmd_label, MM_ERROR, "%s",
|
|
gettxt(_SGI_DMMX_whoami,
|
|
"I don't know who I am."));
|
|
_sgi_ffmtmsg(stderr, 0, cmd_label, MM_FIX, "%s",
|
|
gettxt(_SGI_DMMX_cpmvln,
|
|
"Cmd name must be 'cp', 'mv' or 'ln'"));
|
|
return(1);
|
|
}
|
|
(void)strcpy(cmd_label + 3, cmd);
|
|
(void)setlabel(cmd_label);
|
|
|
|
/*
|
|
* Check for options:
|
|
* cp [-fiprR] file1 [file2 ...] target
|
|
* ln [-sif] file1 [file2 ...] target
|
|
* mv [-if] file1 [file2 ...] target
|
|
* mv [-if] dir1 target
|
|
*/
|
|
while ((c = getopt(argc, argv, (cpy ? "fiprae:RPDtb:v" : ( lnk ? "sif" : "if"))))
|
|
!= EOF) {
|
|
switch (c) {
|
|
case 'a':
|
|
cpy_attribs = 1;
|
|
break;
|
|
#ifdef NOTYET
|
|
case 'M':
|
|
mmap_in = 1;
|
|
pagesize = sysconf(_SC_PAGESIZE);
|
|
break;
|
|
case 'A':
|
|
try_prealloc = 1;
|
|
break;
|
|
#endif
|
|
case 'D':
|
|
do_dio=1;
|
|
break;
|
|
case 'P':
|
|
do_pad=1;
|
|
break;
|
|
case 'b':
|
|
if( (argv_blksz_dio=atoi(argv[optind-1])) <= 0 )
|
|
argv_blksz_dio=0;
|
|
break;
|
|
case 'e':
|
|
if( (rt_size=atoi(argv[optind-1])) <= 0 )
|
|
rt_size=0;
|
|
break;
|
|
case 'v':
|
|
verbose++;
|
|
break;
|
|
case 'f':
|
|
if(cpy)
|
|
fflag = 1;
|
|
else
|
|
silent++;
|
|
if(mve && iflag)
|
|
iflag=0;
|
|
break;
|
|
case 's':
|
|
symlnk = 1;
|
|
break;
|
|
case 'i':
|
|
iflag = 1;
|
|
if(mve && silent)
|
|
silent=0;
|
|
break;
|
|
case 'R':
|
|
Rflag = 1;
|
|
break;
|
|
case 'r':
|
|
rflag = 1;
|
|
break;
|
|
case 'p':
|
|
pflag = 1;
|
|
break;
|
|
case 't':
|
|
do_rtime = 1;
|
|
break;
|
|
default:
|
|
errflg++;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Check for sufficient arguments
|
|
* or a usage error.
|
|
*/
|
|
argc -= optind;
|
|
argv = &argv[optind];
|
|
|
|
if (argc < 2) {
|
|
_sgi_nl_error(SGINL_NOSYSERR, cmd_label,
|
|
gettxt(_SGI_DMMX_insuffargs, "Insufficient arguments"));
|
|
usage(1);
|
|
}
|
|
if (errflg != 0)
|
|
usage(1);
|
|
|
|
/* Real time or direct I/O do not mix with hole copying and
|
|
* memory mapped I/O.
|
|
*/
|
|
if (do_rtime || do_dio) {
|
|
cpy_holes = 0;
|
|
mmap_in = 0;
|
|
}
|
|
|
|
/*
|
|
* If there is more than a source and target,
|
|
* the last argument (the target) must be a directory
|
|
* which really exists.
|
|
*/
|
|
if (argc > 2) {
|
|
if (stat64(argv[argc-1], &s2) < 0) {
|
|
_sgi_nl_error(SGINL_NOSYSERR, cmd_label,
|
|
gettxt(_SGI_DMMX_notfound, "%s not found."),
|
|
argv[argc-1]);
|
|
exit(2);
|
|
}
|
|
|
|
if (!ISDIR(s2)) {
|
|
if (!rflag && !Rflag) {
|
|
_sgi_nl_error(SGINL_NOSYSERR, cmd_label,
|
|
gettxt(_SGI_DMMX_Targetmusbe,
|
|
"Target %s must be %s."),
|
|
argv[argc-1],
|
|
gettxt(_SGI_DMMX_directory, x_directory));
|
|
usage(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Initialize global for XFS filesystem type so we can
|
|
* special case code for it.
|
|
*/
|
|
fstyp_xfs = sysfs(GETFSIND, FSID_XFS);
|
|
|
|
|
|
/*
|
|
* Perform a multiple argument mv|cp|ln by
|
|
* multiple invocations of move().
|
|
*/
|
|
r = 0;
|
|
for (i=0; i<argc-1; i++)
|
|
r += move(argv[i], argv[argc-1]);
|
|
|
|
/*
|
|
* Show errors by nonzero exit code.
|
|
*/
|
|
exit(r?2:0);
|
|
}
|
|
|
|
static int query(omsg, yn)
|
|
char *omsg;
|
|
int yn; /* yes or no default */
|
|
{
|
|
register int i;
|
|
char ibuf[MAX_INPUT];
|
|
char obuf[PATH_MAX+80];
|
|
|
|
_sgi_sfmtmsg(obuf, 0, cmd_label, MM_INFO, "%s", omsg);
|
|
do {
|
|
fprintf(stderr, "%s", obuf);
|
|
i = _sgi_nl_query(stdin, ibuf, yn,
|
|
_SGI_DMMX_query, _SGI_DMMX_querydef,
|
|
_SGI_DMMX_yes, _SGI_DMMX_no);
|
|
} while (i < 0 && !feof(stdin));
|
|
return(feof(stdin) ? yn : i);
|
|
}
|
|
|
|
move(source, target)
|
|
char *source, *target;
|
|
{
|
|
register int c, i, ovrwrt = 0;
|
|
char *sp;
|
|
int sourceexists, targetexists;
|
|
int from, to, oflg;
|
|
int ct, wc, wc_b4;
|
|
struct utimbuf times;
|
|
struct stat64 s1, s2, s3;
|
|
void *fbuf;
|
|
off64_t len, cnt, siz, pos;
|
|
#ifdef NOTYET
|
|
off64_t mmap_pos, mmap_size;
|
|
int do_prealloc;
|
|
void *mmap_buf;
|
|
#endif
|
|
int nextents, extent;
|
|
int rtextsize;
|
|
flock64_t space;
|
|
struct fsxattr fsxattr;
|
|
unsigned blksz_dio;
|
|
unsigned dio_mem;
|
|
unsigned dio_max;
|
|
unsigned dio_min;
|
|
|
|
char buf[PATH_MAX+1];
|
|
mode_t newmode;
|
|
|
|
/*
|
|
* While source or target have trailing /, remove them
|
|
* unless only "/".
|
|
*/
|
|
sp = source + strlen(source);
|
|
if (sp) {
|
|
while (*--sp == '/' && sp > source)
|
|
*sp = '\0';
|
|
}
|
|
sp = target + strlen(target);
|
|
if (sp) {
|
|
while (*--sp == '/' && sp > target)
|
|
*sp = '\0';
|
|
}
|
|
|
|
/*
|
|
* Make sure source file exists.
|
|
*
|
|
* You can move or link a dangling symbolic link.
|
|
* You can also symbolically link to a non-existent file.
|
|
*/
|
|
if (symlnk)
|
|
sourceexists = 0;
|
|
else if (cpy && !Rflag)
|
|
sourceexists = stat64(source, &s1) >= 0;
|
|
else
|
|
sourceexists = lstat64(source, &s1) >= 0;
|
|
if (!sourceexists && !symlnk) {
|
|
Error1(source);
|
|
return(1);
|
|
}
|
|
|
|
times.actime = s1.st_atime;
|
|
times.modtime = s1.st_mtime;
|
|
|
|
/*
|
|
* Make sure source file is not a directory,
|
|
* we don't move() directories, but we do move symlinks,
|
|
* and cp -r can copy directory trees.
|
|
*/
|
|
if (!symlnk && ISDIR(s1) && !mve && !rflag && !Rflag) {
|
|
_sgi_nl_error(SGINL_NOSYSERR, cmd_label,
|
|
cpy ?
|
|
gettxt(_SGI_DMMX_Cannotcopy, "Cannot copy %s %s.")
|
|
:
|
|
gettxt(_SGI_DMMX_Cannotlink, "Cannot link %s %s"),
|
|
gettxt(_SGI_DMMX_directory, x_directory),
|
|
source);
|
|
return(1);
|
|
}
|
|
|
|
/*
|
|
* If it is a move command and we don't have write access
|
|
* to the source's parent then fail.
|
|
*/
|
|
if (mve && accs_parent(source, W_OK|EFF_ONLY_OK) == -1) {
|
|
Error1(source);
|
|
return(1);
|
|
}
|
|
|
|
/*
|
|
* If stat fails, then the target doesn't exist,
|
|
* we will create a new target with default file type of regular.
|
|
*/
|
|
FTYPE(s2) = ((rflag||Rflag) ? FTYPE(s1) : S_IFREG);
|
|
|
|
targetexists = (stat64(target, &s2) >= 0
|
|
|| (!cpy && lstat64(target, &s2) >= 0));
|
|
|
|
if (cpy && do_rtime) {
|
|
char *ptr;
|
|
|
|
if (targetexists) {
|
|
ptr = target;
|
|
} else {
|
|
strcpy(buf, target);
|
|
ptr = strrchr(buf, '/');
|
|
if (ptr) {
|
|
*ptr = '\0';
|
|
} else {
|
|
strcpy(buf, ".");
|
|
}
|
|
ptr = buf;
|
|
}
|
|
|
|
if ((rtextsize = xfsrtextsize( ptr )) <= 0 ) {
|
|
Error1("no XFS real time partition for destination");
|
|
return(1);
|
|
}
|
|
|
|
if ((rt_size != -1) && (rt_size % rtextsize)) {
|
|
_sgi_nl_error(SGINL_NOSYSERR, cmd_label,
|
|
gettxt(_SGI_MMX_badextsize, _SGI_SMMX_badextsize),
|
|
rt_size, rtextsize);
|
|
return(1);
|
|
}
|
|
}
|
|
|
|
if (targetexists) {
|
|
/*
|
|
* If target is a directory,
|
|
* make complete name of new file
|
|
* within that directory.
|
|
*/
|
|
if (ISDIR(s2)) {
|
|
sprintf(buf, "%s/%s", target, basename(source));
|
|
target = buf;
|
|
|
|
/*
|
|
* Reset expectations on type of target
|
|
*/
|
|
FTYPE(s2) = ((rflag||Rflag) ? FTYPE(s1) : S_IFREG);
|
|
}
|
|
|
|
/*
|
|
* If filenames for the source and target are
|
|
* the same and the inodes are the same, it is
|
|
* an error.
|
|
*/
|
|
if (stat64(target, &s2) >= 0
|
|
|| (!cpy && lstat64(target, &s2) >= 0)) {
|
|
if (sourceexists && IDENTICAL(s1,s2)) {
|
|
if (!silent) {
|
|
_sgi_nl_error(SGINL_NOSYSERR, cmd_label,
|
|
gettxt(_SGI_DMMX_areidentical,
|
|
"%s and %s are identical"),
|
|
source, target);
|
|
return(1);
|
|
} else {
|
|
/*
|
|
* Since this is already the desired state
|
|
* and we are silent, return 0 without
|
|
* mentioning an error.
|
|
*/
|
|
return(0);
|
|
}
|
|
}
|
|
if(mve && ISDIR(s1) && !ISDIR(s2)) {
|
|
_sgi_nl_error(SGINL_NOSYSERR, cmd_label,
|
|
gettxt(_SGI_DMMX_cannot_owr,
|
|
"cannot overwrite %s %s"),
|
|
gettxt(_SGI_DMMX_file, x_file),
|
|
target);
|
|
return(1);
|
|
}
|
|
|
|
/*
|
|
* Prevent super-user from overwriting a directory
|
|
* structure with file or symlink of same name.
|
|
*/
|
|
if (lnk && ISDIR(s2) || mve && !ISDIR(s1) && ISDIR(s2)) {
|
|
_sgi_nl_error(SGINL_NOSYSERR, cmd_label,
|
|
gettxt(_SGI_DMMX_cannot_owr,
|
|
"cannot overwrite %s %s"),
|
|
gettxt(_SGI_DMMX_directory, x_directory),
|
|
target);
|
|
return(1);
|
|
}
|
|
|
|
/* if not silent, get confirmation from user */
|
|
if (!silent && iflag && (!ISDIR(s2) || !cpy)) {
|
|
sprintf(xmsgbuf,
|
|
gettxt(_SGI_DMMX_overwrite,
|
|
"overwrite %s? "),
|
|
target);
|
|
/* save if we decide to overwrite or not */
|
|
ovrwrt = query(xmsgbuf, SGINL_DEFNO);
|
|
if( ! ovrwrt)
|
|
return(1);
|
|
}
|
|
|
|
/*
|
|
* Because copy doesn't unlink target,
|
|
* treat it separately.
|
|
*/
|
|
if (cpy)
|
|
goto skip;
|
|
|
|
/*
|
|
* If the user does not have access to
|
|
* the target, ask them -- if it is not
|
|
* silent and user invoked command
|
|
* interactively. Avoid this if the target
|
|
* is actually a symlink.
|
|
*/
|
|
if (lstat64(target, &s3) >= 0
|
|
&& !ISLNK(s3)
|
|
&& access(target, W_OK|EFF_ONLY_OK) < 0
|
|
&& isatty(fileno(stdin))
|
|
&& !iflag
|
|
&& !symlnk
|
|
&& !silent) {
|
|
sprintf(xmsgbuf,
|
|
gettxt(_SGI_DMMX_file_mode,
|
|
"%s: %o mode? "),
|
|
target,
|
|
FMODE(s2) & MODEBITS);
|
|
i = query(xmsgbuf, SGINL_DEFNO);
|
|
if( !i)
|
|
return(1);
|
|
}
|
|
/*
|
|
* Do not unlink target if regular link and not
|
|
* silent and interactive.
|
|
*/
|
|
if (lnk && !symlnk && !silent && !iflag) {
|
|
_sgi_nl_error(SGINL_SYSERR2, cmd_label,
|
|
gettxt(_SGI_DMMX_Notunlink,
|
|
"May not unlink existing %s %s"),
|
|
target,"");
|
|
return(1);
|
|
}
|
|
|
|
/*
|
|
* Attempt to unlink the target if request is a
|
|
* regular link. If a symlink, then unlink the
|
|
* target only if ovrwrt has been set.
|
|
*/
|
|
if (lnk && (!symlnk || ovrwrt || silent) &&
|
|
unlink(target) < 0) {
|
|
_sgi_nl_error(SGINL_SYSERR2, cmd_label,
|
|
gettxt(_SGI_DMMX_Cannotunlink,
|
|
"Cannot unlink %s"),
|
|
target);
|
|
return(1);
|
|
}
|
|
|
|
}
|
|
}
|
|
skip:
|
|
/*
|
|
* Either the target doesn't exist,
|
|
* or this is a copy command ...
|
|
*/
|
|
if (ISDIR(s1) && (rflag || Rflag)) { /* do recursive copy */
|
|
return rcopy(source, target, ×);
|
|
}
|
|
|
|
if (lnk) {
|
|
register int result = 0;
|
|
static int (*linkfunc)(const char *, const char *);
|
|
if (!symlnk && !linkfunc) {
|
|
char *xpg = getenv("_XPG");
|
|
if (xpg && atoi(xpg) > 0)
|
|
linkfunc = linkfollow;
|
|
else
|
|
linkfunc = link;
|
|
}
|
|
if (symlnk) {
|
|
if (symlink(source, target) < 0) {
|
|
if (errno == EEXIST)
|
|
_sgi_nl_error(SGINL_NOSYSERR, cmd_label,
|
|
gettxt(_SGI_DMMX_CannotCreat,
|
|
"Cannot create %s: File exists"),
|
|
target);
|
|
else
|
|
Error1(target);
|
|
result = 1;
|
|
}
|
|
} else if ((*linkfunc)(source,target) < 0) {
|
|
if (errno == EXDEV) {
|
|
_sgi_nl_error(SGINL_NOSYSERR, cmd_label,
|
|
gettxt(_SGI_DMMX_diff_filsys,
|
|
"%s %s: different file systems"),
|
|
source, target);
|
|
return (1);
|
|
}
|
|
_sgi_nl_error(SGINL_SYSERR2, cmd_label,
|
|
gettxt(_SGI_DMMX_Cannotlinkto, "Cannot link %s to %s"),
|
|
target,
|
|
source);
|
|
result = 1;
|
|
}
|
|
return(result);
|
|
}
|
|
|
|
if (mve) {
|
|
if (rename(source, target) == 0)
|
|
return (0);
|
|
|
|
if (errno != EXDEV) {
|
|
Error1((errno == ENOENT && !targetexists) ? target : source);
|
|
return (1);
|
|
}
|
|
/*
|
|
* if source is a symbolic link, do the 'copy' now.
|
|
*/
|
|
if (ISLNK(s1)) {
|
|
char lbuf[MAXPATHLEN];
|
|
register int len;
|
|
register int result = 1;
|
|
|
|
len = readlink(source,lbuf,sizeof(lbuf)-1);
|
|
if (len >= sizeof(lbuf)) {
|
|
lbuf[sizeof(lbuf)-1] = '\0';
|
|
_sgi_nl_error(SGINL_NOSYSERR, cmd_label,
|
|
gettxt(_SGI_DMMX_syml_2long,
|
|
"Symbolic link %s -> %s too long"),
|
|
source,
|
|
lbuf);
|
|
} else if (len < 0) {
|
|
Error1(source);
|
|
#ifdef sgi
|
|
} else if (unlink(target) < 0 && errno != ENOENT) {
|
|
#else
|
|
} else if (unlink(target) < 0) {
|
|
#endif
|
|
Error1(target);
|
|
} else if (lbuf[len] = '\0',
|
|
symlink(lbuf, target) < 0) {
|
|
Error1(target);
|
|
#ifdef sgi
|
|
} else if (unlink(source) < 0) {
|
|
Error1(source);
|
|
#endif
|
|
} else {
|
|
result = 0;
|
|
}
|
|
return (result);
|
|
}
|
|
if (ISDIR(s1)) {
|
|
int ret;
|
|
mode_t mask = umask(0);
|
|
|
|
/* Check if target exists */
|
|
if(stat64(target, &s2) >= 0) {
|
|
if(rmdir(target) < 0) {
|
|
if(errno == EEXIST)
|
|
errno = ENOTEMPTY;
|
|
Error1(target);
|
|
return(1);
|
|
}
|
|
}
|
|
strcpy(xdev_target,target);
|
|
strcpy(xdev_source,source);
|
|
|
|
/* Simulate a cp command with -pR flags */
|
|
mve = 0;
|
|
cpy = pflag = Rflag = 1;
|
|
xdev_state = 1;
|
|
|
|
signal(SIGINT, xdev_quit);
|
|
signal(SIGQUIT, xdev_quit);
|
|
signal(SIGTERM, xdev_quit);
|
|
|
|
/* Walk the source tree for the copy to target */
|
|
if((ret = nftw64(source, nftw_cp, OPEN_MAX,
|
|
FTW_DEPTH|FTW_PHYS|FTW_MOUNT)) != 0 ) {
|
|
_sgi_nl_error(SGINL_NOSYSERR, cmd_label,
|
|
gettxt(_SGI_DMMX_xpg_mv_partdest,
|
|
"Warning - incomplete copy to target tree: %s"),target);
|
|
mve = 1;
|
|
cpy = pflag = Rflag = 0;
|
|
umask(mask);
|
|
return(1);
|
|
}
|
|
umask(mask);
|
|
mve = 1;
|
|
cpy = pflag = Rflag = 0;
|
|
|
|
/* Walk the source tree for removal */
|
|
|
|
xdev_state = 2;
|
|
if((ret = nftw64(source, nftw_rm, OPEN_MAX,
|
|
FTW_DEPTH|FTW_PHYS|FTW_MOUNT)) != 0 ) {
|
|
_sgi_nl_error(SGINL_NOSYSERR, cmd_label,
|
|
gettxt(_SGI_DMMX_xpg_mv_partsrc,
|
|
"Warning - incomplete removal of source tree: %s"),source);
|
|
return(1);
|
|
}
|
|
signal(SIGINT, SIG_DFL);
|
|
signal(SIGQUIT, SIG_DFL);
|
|
signal(SIGTERM, SIG_DFL);
|
|
return(0);
|
|
}
|
|
if(!ISREG(s1))
|
|
{
|
|
int create_error = 0;
|
|
int rm_error = 0;
|
|
int mod_error = 0;
|
|
|
|
if(accs_parent(target, W_OK|EFF_ONLY_OK) == -1) {
|
|
_sgi_nl_error(SGINL_SYSERR2, cmd_label,
|
|
gettxt(_SGI_DMMX_CannotAccess,
|
|
"Cannot access %s"), target);
|
|
return(1);
|
|
}
|
|
|
|
switch(FMODE(s1)&S_IFMT)
|
|
{
|
|
case S_IFIFO:
|
|
if(mkfifo(target,FMODE(s1)) < 0)
|
|
++create_error;
|
|
else if(unlink(source) < 0)
|
|
++rm_error;
|
|
break;
|
|
case S_IFCHR: /* character special */
|
|
case S_IFBLK: /* block special */
|
|
case S_IFSOCK: /* socket */
|
|
if(mknod(target,FMODE(s1),s1.st_rdev) < 0)
|
|
++create_error;
|
|
else if(unlink(source) < 0)
|
|
++rm_error;
|
|
break;
|
|
default:
|
|
++create_error;
|
|
break;
|
|
}
|
|
if( rm_error ) {
|
|
_sgi_nl_error(SGINL_SYSERR2, cmd_label,
|
|
gettxt(_SGI_DMMX_Cannotunlink,
|
|
"Cannot unlink %s"), target);
|
|
return (1);
|
|
}
|
|
if( create_error ) {
|
|
_sgi_nl_error(SGINL_SYSERR2, cmd_label,
|
|
gettxt(_SGI_DMMX_CannotCreat,
|
|
"Cannot create %s"), target);
|
|
return (1);
|
|
}
|
|
if(utime(target, ×) < 0)
|
|
_sgi_nl_error(SGINL_SYSERR2, cmd_label,
|
|
gettxt(_SGI_DMMX_xpg_mv_utime,
|
|
"%s: Cannot update target times"),target);
|
|
if(chown(target, FUSER(s1), FGROUP(s1)) < 0) {
|
|
_sgi_nl_error(SGINL_SYSERR2, cmd_label,
|
|
gettxt(_SGI_DMMX_xpg_mv_chown,
|
|
"%s: Cannot change target ownership to uid(%d) gid(%d)"),
|
|
target, FUSER(s1), FGROUP(s1));
|
|
if(chmod(target, FMODE(s1)&(~(S_ISUID|S_ISGID))) < 0)
|
|
_sgi_nl_error(SGINL_SYSERR2, cmd_label,
|
|
gettxt(_SGI_DMMX_xpg_mv_chmod,
|
|
"%s: Cannot change target mode to (%o)"),
|
|
target, FMODE(s1));
|
|
}
|
|
else if(chmod(target, FMODE(s1)) < 0)
|
|
_sgi_nl_error(SGINL_SYSERR2, cmd_label,
|
|
gettxt(_SGI_DMMX_xpg_mv_chmod,
|
|
"%s: Cannot change target mode to (%o)"),
|
|
target, FMODE(s1));
|
|
return(0);
|
|
}
|
|
}
|
|
if(!S_ISREG(s1.st_mode) && cpy && Rflag)
|
|
{
|
|
int create_error = 0;
|
|
int syml = 0;
|
|
|
|
switch(FMODE(s1)&S_IFMT)
|
|
{
|
|
case S_IFIFO:
|
|
if(mkfifo(target,FMODE(s1)) < 0)
|
|
++create_error;
|
|
break;
|
|
case S_IFCHR: /* character special */
|
|
case S_IFBLK: /* block special */
|
|
case S_IFSOCK: /* socket */
|
|
if(mknod(target,FMODE(s1),s1.st_rdev) < 0)
|
|
++create_error;
|
|
break;
|
|
case S_IFLNK: /* symbolic link */
|
|
{
|
|
char lbuf[MAXPATHLEN];
|
|
register int len;
|
|
|
|
++syml;
|
|
|
|
len = readlink(source,lbuf,sizeof(lbuf)-1);
|
|
if (len >= sizeof(lbuf))
|
|
{
|
|
lbuf[sizeof(lbuf)-1] = '\0';
|
|
_sgi_nl_error(SGINL_NOSYSERR, cmd_label,
|
|
gettxt(_SGI_DMMX_syml_2long,
|
|
"Symbolic link %s -> %s too long"),
|
|
source,lbuf);
|
|
++create_error;
|
|
}
|
|
else if (len < 0)
|
|
++create_error;
|
|
else if (lbuf[len] = '\0', symlink(lbuf, target) < 0)
|
|
++create_error;
|
|
break;
|
|
}
|
|
default:
|
|
++create_error;
|
|
break;
|
|
}
|
|
if( create_error )
|
|
{
|
|
_sgi_nl_error(SGINL_SYSERR2, cmd_label,
|
|
gettxt(_SGI_DMMX_CannotCreat,
|
|
"Cannot create %s"), target);
|
|
return (1);
|
|
}
|
|
if(!syml && pflag)
|
|
{
|
|
if(utime(target, ×) < 0)
|
|
_sgi_nl_error(SGINL_SYSERR2, cmd_label,
|
|
gettxt(_SGI_DMMX_xpg_mv_utime,
|
|
"%s: Cannot update target times"),target);
|
|
if(chown(target, FUSER(s1), FGROUP(s1)) < 0) {
|
|
_sgi_nl_error(SGINL_SYSERR2, cmd_label,
|
|
gettxt(_SGI_DMMX_xpg_mv_chown,
|
|
"%s: Cannot change target ownership to uid(%d) gid(%d)"),
|
|
target, FUSER(s1), FGROUP(s1));
|
|
if(chmod(target,FMODE(s1)&(~(S_ISUID|S_ISGID))) < 0)
|
|
_sgi_nl_error(SGINL_SYSERR2, cmd_label,
|
|
gettxt(_SGI_DMMX_xpg_mv_chmod,
|
|
"%s: Cannot change target mode to (%o)"),
|
|
target, FMODE(s1));
|
|
}
|
|
else if(chmod(target,FMODE(s1)) < 0)
|
|
_sgi_nl_error(SGINL_SYSERR2, cmd_label,
|
|
gettxt(_SGI_DMMX_xpg_mv_chmod,
|
|
"%s: Cannot change target mode to (%o)"),
|
|
target, FMODE(s1));
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
/*
|
|
* Attempt to open source for copy.
|
|
*/
|
|
if ((from = open(source, 0)) < 0) {
|
|
CannotOpen(source);
|
|
return (1);
|
|
}
|
|
|
|
if (do_rtime) do_dio = 1;
|
|
if(!do_dio || !cpy) do_dio=0;
|
|
else {
|
|
if( (fcntl(from,F_SETFL,0 | FDIRECT)) == EINVAL ) {
|
|
if(verbose) {
|
|
_sgi_nl_error(SGINL_NOSYSERR, cmd_label,
|
|
gettxt(_SGI_DMMX_nodiosupp,
|
|
_SGI_SMMX_nodiosupp), source);
|
|
}
|
|
do_dio=0;
|
|
close(from);
|
|
from = open(source, 0);
|
|
}
|
|
else {
|
|
if( (fcntl(from,F_DIOINFO, &from_dio)) == -1 ) {
|
|
if(verbose) {
|
|
_sgi_nl_error(SGINL_SYSERR, cmd_label,
|
|
gettxt(_SGI_DMMX_dioerror,
|
|
_SGI_SMMX_dioerror), source);
|
|
}
|
|
do_dio=0;
|
|
close(from);
|
|
from = open(source,0);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If we are copying to complete a cross-device mv, and the
|
|
* target is a symbolic link, then remove the link before
|
|
* trying to copy the file.
|
|
*/
|
|
if (!cpy) {
|
|
struct stat64 ls;
|
|
if (lstat64(target, &ls) >=0 && ISLNK(ls)) {
|
|
if (unlink(target) < 0) {
|
|
Error1(target);
|
|
return (1);
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
* Save a flag to indicate target existed.
|
|
*/
|
|
if(!mve && !pflag)
|
|
oflg = access(target, F_OK|EFF_ONLY_OK);
|
|
/*
|
|
* Attempt to create a target.
|
|
*/
|
|
if (silent) {
|
|
/*
|
|
* Without this, cross device moves where we are non-superuser
|
|
* and the target is not writable fail.
|
|
*/
|
|
(void)unlink(target);
|
|
}
|
|
again:
|
|
if ((to = creat(target, 0666)) < 0)
|
|
{
|
|
if(!oflg && fflag && ISREG(s1))
|
|
{
|
|
if(unlink(target) < 0)
|
|
{
|
|
_sgi_nl_error(SGINL_SYSERR2, cmd_label,
|
|
gettxt(_SGI_DMMX_Cannotunlink,
|
|
"Cannot unlink %s"),
|
|
target);
|
|
return(1);
|
|
}
|
|
oflg = 1;
|
|
goto again;
|
|
}
|
|
else
|
|
{
|
|
_sgi_nl_error(SGINL_SYSERR2, cmd_label,
|
|
gettxt(_SGI_DMMX_CannotCreat, "Cannot create %s"), target);
|
|
close(from);
|
|
return (1);
|
|
}
|
|
}
|
|
if(do_dio) {
|
|
if((fcntl(to,F_SETFL,FDIRECT)) == EINVAL) {
|
|
if(verbose) {
|
|
_sgi_nl_error(SGINL_NOSYSERR, cmd_label,
|
|
gettxt(_SGI_DMMX_nodiosupp,
|
|
_SGI_SMMX_nodiosupp), target);
|
|
}
|
|
do_dio=0;
|
|
close(from);
|
|
from = open(source,0);
|
|
}
|
|
else {
|
|
if( (fcntl(to,F_DIOINFO, &to_dio)) == -1 ) {
|
|
if(verbose) {
|
|
_sgi_nl_error(SGINL_SYSERR, cmd_label,
|
|
gettxt(_SGI_DMMX_dioerror,
|
|
_SGI_SMMX_dioerror), target);
|
|
}
|
|
do_dio=0;
|
|
close(from);
|
|
from = open(source,0);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Block copy from source to target.
|
|
*/
|
|
|
|
|
|
/*
|
|
* First work out the extent map - this will return everything
|
|
* in one lump if there are no holes, or we are not running XFS.
|
|
*/
|
|
nextents = read_fd_bmap(from, to, &s1, &s2);
|
|
#ifdef NOTYET
|
|
if (get_fs_type(to) == FS_XFS) {
|
|
do_prealloc = try_prealloc;
|
|
} else {
|
|
do_prealloc = 0;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* If we created a target, or are mv'ing,
|
|
* set target permissions to the source
|
|
* before any copying so that any partially copied
|
|
* file will have the source's permissions (at most)
|
|
* or umask permissions whichever is the most restrictive.
|
|
*/
|
|
|
|
if (oflg || mve || pflag) {
|
|
newmode = FMODE(s1);
|
|
if (cpy && oflg && !pflag) {
|
|
mode_t mask = umask(0);
|
|
umask(mask);
|
|
newmode &= ~mask;
|
|
}
|
|
if(chmod(target, newmode)<0)
|
|
_sgi_nl_error(SGINL_SYSERR2, cmd_label,
|
|
gettxt(_SGI_DMMX_xpg_mv_chmod,
|
|
"%s: Cannot change target mode to (%o)"),
|
|
target, newmode);
|
|
}
|
|
|
|
if( !do_dio) {
|
|
if (argv_blksz_dio) {
|
|
blksz_dio = argv_blksz_dio;
|
|
} else {
|
|
blksz_dio=BUFFER_SIZE;
|
|
}
|
|
dio_max = blksz_dio;
|
|
dio_min = 4096;
|
|
}
|
|
else { /* Hola ... */
|
|
|
|
dio_mem=from_dio.d_mem > to_dio.d_mem ? from_dio.d_mem : to_dio.d_mem ;
|
|
dio_min=from_dio.d_miniosz > to_dio.d_miniosz ? from_dio.d_miniosz : to_dio.d_miniosz ;
|
|
dio_max=from_dio.d_maxiosz < to_dio.d_maxiosz ? from_dio.d_maxiosz : to_dio.d_maxiosz ;
|
|
if(verbose>1) {
|
|
_sgi_nl_error(SGINL_NOSYSERR, cmd_label,
|
|
gettxt(_SGI_DMMX_diosizes,
|
|
_SGI_SMMX_diosizes), dio_min,dio_max);
|
|
}
|
|
|
|
if(argv_blksz_dio == 0) blksz_dio=dio_max;
|
|
else {
|
|
blksz_dio = argv_blksz_dio;
|
|
|
|
if( blksz_dio%dio_min != 0 ||
|
|
blksz_dio < dio_min ||
|
|
blksz_dio > dio_max ) {
|
|
if(verbose) {
|
|
_sgi_nl_error(SGINL_NOSYSERR, cmd_label,
|
|
gettxt(_SGI_DMMX_diousemax,
|
|
_SGI_SMMX_diousemax));
|
|
}
|
|
blksz_dio=dio_max;
|
|
}
|
|
}
|
|
|
|
if (do_rtime) {
|
|
fsxattr.fsx_xflags = XFS_XFLAG_REALTIME;
|
|
if (rt_size != -1)
|
|
fsxattr.fsx_extsize = rt_size;
|
|
else
|
|
fsxattr.fsx_extsize = rtextsize;
|
|
|
|
if (fcntl(to, F_FSSETXATTR, &fsxattr) < 0) {
|
|
_sgi_nl_error(SGINL_SYSERR2, cmd_label,
|
|
gettxt(_SGI_DMMX_rtiofail,
|
|
_SGI_SMMX_rtiofail), target);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Make sure the buffer size we use is sensible */
|
|
if ((s1.st_size > BUFFER_SIZE) && !argv_blksz_dio) {
|
|
while ((blksz_dio > s1.st_size) && (blksz_dio > dio_min)) {
|
|
blksz_dio >>= 1;
|
|
}
|
|
}
|
|
|
|
if (s1.st_size < blksz_dio) {
|
|
while ((blksz_dio > s1.st_size) && (blksz_dio > dio_min)) {
|
|
blksz_dio >>= 1;
|
|
}
|
|
if (blksz_dio < s1.st_size) {
|
|
blksz_dio <<= 1;
|
|
}
|
|
}
|
|
|
|
if (do_dio) {
|
|
fbuf = (char *)memalign(dio_mem,blksz_dio);
|
|
} else {
|
|
fbuf= malloc(blksz_dio);
|
|
}
|
|
|
|
if(verbose>1) {
|
|
_sgi_nl_error(SGINL_NOSYSERR, cmd_label,
|
|
gettxt(_SGI_DMMX_diousing, _SGI_SMMX_diousing),
|
|
blksz_dio);
|
|
}
|
|
|
|
for (extent = 0; extent < nextents; extent++) {
|
|
pos = outmap[extent].bmv_offset;
|
|
if (cpy_holes && (outmap[extent].bmv_block == -1)) {
|
|
space.l_whence = 0;
|
|
space.l_start = pos;
|
|
space.l_len = outmap[extent].bmv_length;
|
|
if (fcntl(to, F_UNRESVSP64, &space) < 0) {
|
|
Error1("Truncating extent");
|
|
}
|
|
lseek64(to, outmap[extent].bmv_length, SEEK_CUR);
|
|
lseek64(from, outmap[extent].bmv_length, SEEK_CUR);
|
|
continue;
|
|
}
|
|
|
|
#ifdef NOTYET
|
|
if (do_prealloc) {
|
|
space.l_whence = 1;
|
|
space.l_start = 0;
|
|
space.l_len = outmap[extent].bmv_length;
|
|
if (fcntl(to, F_RESVSP64, &space) < 0) {
|
|
Error1("reserving file space");
|
|
}
|
|
}
|
|
#endif
|
|
|
|
for (cnt = outmap[extent].bmv_length; cnt > 0;
|
|
cnt -= ct, pos += ct) {
|
|
if (!do_dio) {
|
|
ct = min(blksz_dio, cnt);
|
|
} else {
|
|
ct = blksz_dio;
|
|
}
|
|
|
|
#ifdef NOTYET
|
|
if (mmap_in) {
|
|
mmap_pos = pos & ~(pagesize-1);
|
|
mmap_size = (ct + (pos - mmap_pos) +
|
|
pagesize - 1) & ~(pagesize-1);
|
|
mmap_buf = mmap64(NULL, mmap_size, PROT_READ,
|
|
MAP_SHARED, from, mmap_pos);
|
|
|
|
if (mmap_buf == MAP_FAILED) {
|
|
perror("on mmap call");
|
|
exit(1);
|
|
}
|
|
fbuf = (void *)((char *)(mmap_buf) + pos -
|
|
mmap_pos);
|
|
} else {
|
|
ct = read(from, fbuf, ct);
|
|
}
|
|
#else
|
|
ct = read(from, fbuf, ct);
|
|
#endif
|
|
if (ct == 0) {
|
|
/* EOF, stop trying to read */
|
|
extent = nextents;
|
|
break;
|
|
}
|
|
|
|
|
|
/* Ensure we do direct I/O to correct block
|
|
* boundaries.
|
|
*/
|
|
if (do_dio && (ct != blksz_dio)) {
|
|
wc = ct + dio_min - (ct % dio_min);
|
|
if (do_pad) {
|
|
bzero(((char *)fbuf) + wc - ct,
|
|
wc - ct);
|
|
}
|
|
} else {
|
|
wc = ct;
|
|
}
|
|
wc_b4 = wc;
|
|
if (ct < 0 || ((wc = write(to, fbuf, wc)) != wc_b4)) {
|
|
if (ct < 0)
|
|
_sgi_nl_error(SGINL_SYSERR2, cmd_label,
|
|
gettxt(_SGI_DMMX_CannotRead,
|
|
"bad read from %s"),
|
|
source);
|
|
else if (wc < 0)
|
|
BadWrite(target);
|
|
else {
|
|
/*
|
|
* Might be out of space
|
|
*
|
|
* Try to finish write
|
|
*/
|
|
int resid = ct-wc;
|
|
|
|
if ((wc = write(to, ((char *)fbuf)+wc,
|
|
resid)) == resid) {
|
|
/* worked on second attempt? */
|
|
continue;
|
|
}
|
|
else
|
|
if (wc < 0) {
|
|
BadWrite(target);
|
|
} else {
|
|
_sgi_nl_error(SGINL_NOSYSERR, cmd_label,
|
|
gettxt(_SGI_DMMX_bad_copy,
|
|
"bad copy to %s"),
|
|
target);
|
|
}
|
|
}
|
|
/*
|
|
* If target is a regular file,
|
|
* unlink the bad file.
|
|
*/
|
|
if (ISREG(s2))
|
|
unlink(target);
|
|
#ifdef NOTYET
|
|
if (!mmap_in) {
|
|
free(fbuf);
|
|
} else {
|
|
munmap(mmap_buf, mmap_size);
|
|
}
|
|
#else
|
|
free(fbuf);
|
|
#endif
|
|
return (1);
|
|
}
|
|
#ifdef NOTYET
|
|
if (mmap_in) {
|
|
munmap(mmap_buf, mmap_size);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
if(do_dio && !do_pad) ftruncate64(to,s1.st_size);
|
|
#ifdef NOTYET
|
|
if (!mmap_in) {
|
|
free(fbuf);
|
|
}
|
|
#else
|
|
free(fbuf);
|
|
#endif
|
|
|
|
if (cpy_attribs) {
|
|
clone_attribs(from, to);
|
|
}
|
|
|
|
(void)close(from);
|
|
if (close(to) < 0) {
|
|
BadWrite(target);
|
|
if (ISREG(s2))
|
|
unlink(target);
|
|
return (1);
|
|
}
|
|
/*
|
|
* If it was a move, leave times alone.
|
|
*/
|
|
if (mve || pflag) {
|
|
if(utime(target, ×) < 0)
|
|
_sgi_nl_error(SGINL_SYSERR2, cmd_label,
|
|
gettxt(_SGI_DMMX_xpg_mv_utime,
|
|
"%s: Cannot update target times"),target);
|
|
}
|
|
|
|
/*
|
|
* Attempt to unlink the source.
|
|
*/
|
|
/* unlink source iff rename failed and we decided to copy */
|
|
if (!cpy && unlink(source) < 0) {
|
|
Error1(source);
|
|
|
|
/*
|
|
* If we can't unlink the source, assume we lack permission.
|
|
* Remove the target, as we may have copied it erroneously:
|
|
* (1) from an NFS filesystem, running as root. In this case
|
|
* the call to accs_parent(source) succeeds based on the
|
|
* client credentials, but unlink(source) has just failed
|
|
* because the server uid for client root is usually -2.
|
|
* (2) because after the accs_parent(source) succeeded, we
|
|
* lost a race to someone who removed write permission
|
|
* from the source directory.
|
|
*/
|
|
if (stat64(source, &s1) == 0)
|
|
(void) unlink(target);
|
|
return(1);
|
|
}
|
|
|
|
/*
|
|
* If it was a move, sets its owner to the source
|
|
*/
|
|
if (mve || pflag) {
|
|
if(chown(target, FUSER(s1), FGROUP(s1)) < 0) {
|
|
_sgi_nl_error(SGINL_SYSERR2, cmd_label,
|
|
gettxt(_SGI_DMMX_xpg_mv_chown,
|
|
"%s: Cannot change target ownership to uid(%d) gid(%d)"),
|
|
target, FUSER(s1), FGROUP(s1));
|
|
newmode &= ~(S_ISUID|S_ISGID);
|
|
}
|
|
}
|
|
if (oflg || mve || pflag)
|
|
chmod(target, newmode); /* Do this now: chown strips S_ISUID/S_ISGID */
|
|
return(0);
|
|
}
|
|
|
|
|
|
accs_parent(name, amode)
|
|
register char *name;
|
|
register int amode;
|
|
{
|
|
register char c, *p, *q;
|
|
char buf[PATH_MAX+1];
|
|
|
|
p = q = buf;
|
|
|
|
/*
|
|
* Copy name into buf and set 'q' to point to the last
|
|
* delimiter within name.
|
|
*/
|
|
while (c = *p++ = *name++)
|
|
if (c == '/')
|
|
q = p-1;
|
|
|
|
/*
|
|
* If the name had no '\' or was "\" then leave it alone,
|
|
* otherwise null the name at the last delimiter.
|
|
*/
|
|
if (q == buf && *q == '/')
|
|
q++;
|
|
*q = '\0';
|
|
|
|
/*
|
|
* Find the access of the parent.
|
|
* If no parent specified, use dot.
|
|
*/
|
|
return access(buf[0] ? buf : ".", amode);
|
|
}
|
|
|
|
/* recursive copy */
|
|
int
|
|
rcopy(from, to, timep)
|
|
char *from, *to;
|
|
struct utimbuf *timep;
|
|
{
|
|
register DIR *fold;
|
|
register dirent64_t *dp;
|
|
int errs = 0;
|
|
struct stat64 t_sbuf, f_sbuf;
|
|
int made_dir = 0;
|
|
char fromname[MAXPATHLEN+1];
|
|
|
|
fold = opendir(from);
|
|
if (fold == 0) {
|
|
CannotOpen(from);
|
|
return (1);
|
|
}
|
|
|
|
if (stat64(to, &t_sbuf) < 0) {
|
|
mode_t mask = umask(0);
|
|
umask(mask);
|
|
if(pflag)
|
|
mask=0;
|
|
made_dir = 1;
|
|
FMODE(f_sbuf) = 0;
|
|
if (stat64(from, &f_sbuf) < 0)
|
|
Error1(from);
|
|
if (mkdir(to, FMODE(f_sbuf)&(~mask)|S_IRWXU) < 0) {
|
|
Error1(to);
|
|
closedir(fold);
|
|
return(1);
|
|
}
|
|
} else if (!ISDIR(t_sbuf)) {
|
|
usage(0);
|
|
}
|
|
|
|
for (;;) {
|
|
dp = readdir64(fold);
|
|
if (dp == NULL) {
|
|
closedir(fold);
|
|
if (made_dir) {
|
|
mode_t mask = umask(0);
|
|
umask(mask);
|
|
if(pflag)
|
|
mask=0;
|
|
if (chmod(to, FMODE(f_sbuf)&(~mask)) < 0) {
|
|
_sgi_nl_error(SGINL_SYSERR2, cmd_label,
|
|
gettxt(_SGI_DMMX_xpg_mv_chmod,
|
|
"%s: Cannot change target mode to (%o)"),
|
|
to, FMODE(f_sbuf)&(~mask));
|
|
errs++;
|
|
} else if (pflag) {
|
|
if(utime(to, timep) < 0)
|
|
_sgi_nl_error(SGINL_SYSERR2, cmd_label,
|
|
gettxt(_SGI_DMMX_xpg_mv_utime,
|
|
"%s: Cannot update target times"),to);
|
|
if(chown(to, FUSER(f_sbuf), FGROUP(f_sbuf))<0)
|
|
_sgi_nl_error(SGINL_SYSERR2, cmd_label,
|
|
gettxt(_SGI_DMMX_xpg_mv_chown,
|
|
"%s: Cannot change target ownership to uid(%d) gid(%d)"),
|
|
to, FUSER(f_sbuf), FGROUP(f_sbuf) );
|
|
}
|
|
}
|
|
return (errs);
|
|
}
|
|
if (dp->d_ino == 0
|
|
|| !strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
|
|
continue;
|
|
|
|
if (strlen(from) + 1 + strlen(dp->d_name) >= sizeof(fromname)-1) {
|
|
_sgi_nl_error(SGINL_NOSYSERR, cmd_label,
|
|
gettxt(_SGI_DMMX_Name2long, "%s/%s: Name too long"),
|
|
from,
|
|
dp->d_name);
|
|
errs++;
|
|
continue;
|
|
}
|
|
(void) sprintf(fromname, "%s/%s", from, dp->d_name);
|
|
|
|
errs += move(fromname, to);
|
|
}
|
|
}
|
|
|
|
char *
|
|
basename(name)
|
|
register char *name;
|
|
{
|
|
register char *p;
|
|
|
|
/*
|
|
* Return just the file name given the complete path.
|
|
*/
|
|
p = name;
|
|
|
|
/*
|
|
* While there are characters left,
|
|
* set name to start after last /.
|
|
*/
|
|
while(*p)
|
|
if(*p++ == '/' && *p)
|
|
name = p;
|
|
return(name);
|
|
}
|
|
|
|
int
|
|
nftw_cp(const char *path, const struct stat64 *snftw, int nftw_flag, struct FTW *Snftw)
|
|
{
|
|
char target_path[MAXPATHLEN+1];
|
|
char dir_path[MAXPATHLEN+1];
|
|
char nftw_path[MAXPATHLEN+1];
|
|
char *b;
|
|
int c;
|
|
struct utimbuf times;
|
|
|
|
/* Determine full destination path */
|
|
strcpy(target_path,xdev_target);
|
|
|
|
strcpy(nftw_path,path);
|
|
b = &nftw_path[strlen(nftw_path)-1];
|
|
for(c=0; c < (Snftw->level); c++) {
|
|
while(*b != '/' && *b != 0)--b;
|
|
if(*b != 0)--b;
|
|
}
|
|
if( (strlen(target_path)+strlen(b)+1) > MAXPATHLEN) {
|
|
_sgi_nl_error(SGINL_NOSYSERR, cmd_label,
|
|
gettxt(_SGI_DMMX_Name2long, "%s/%s: Name too long"),
|
|
target_path,b);
|
|
return(1);
|
|
}
|
|
strcat(target_path,++b);
|
|
|
|
switch(nftw_flag)
|
|
{
|
|
case FTW_SL: /* symbolic link */
|
|
case FTW_SLN: /* symbolic link that points to nonexistent file */
|
|
case FTW_F: /* file */
|
|
|
|
/* Creat directories up to target if needed */
|
|
|
|
strcpy(dir_path,target_path);
|
|
b = &dir_path[strlen(dir_path)-1];
|
|
while(*b != '/' && *b != 0) *b-- = 0;
|
|
*b = 0;
|
|
if(mkdirp(dir_path,0777) < 0) {
|
|
Error1(dir_path);
|
|
return(1);
|
|
}
|
|
return move(path,target_path);
|
|
|
|
case FTW_DP: /* directory pre visited */
|
|
case FTW_D: /* directory */
|
|
strcpy(dir_path,target_path);
|
|
if(mkdirp(dir_path,0777) < 0) {
|
|
Error1(dir_path);
|
|
return(1);
|
|
}
|
|
times.actime = snftw->st_atime;
|
|
times.modtime = snftw->st_mtime;
|
|
if(utime(dir_path, ×) < 0)
|
|
_sgi_nl_error(SGINL_SYSERR2, cmd_label,
|
|
gettxt(_SGI_DMMX_xpg_mv_utime,
|
|
"%s: Cannot update target times"),dir_path);
|
|
if(chown(dir_path, snftw->st_uid, snftw->st_gid) < 0)
|
|
_sgi_nl_error(SGINL_SYSERR2, cmd_label,
|
|
gettxt(_SGI_DMMX_xpg_mv_chown,
|
|
"%s: Cannot change target ownership to uid(%d) gid(%d)"),
|
|
dir_path, snftw->st_uid, snftw->st_gid);
|
|
if(chmod(dir_path,snftw->st_mode) < 0)
|
|
_sgi_nl_error(SGINL_SYSERR2, cmd_label,
|
|
gettxt(_SGI_DMMX_xpg_mv_chmod,
|
|
"%s: Cannot change target mode to (%o)"),
|
|
dir_path, snftw->st_mode);
|
|
return(0);
|
|
|
|
case FTW_DNR: /* directory without read permission */
|
|
_sgi_nl_error(SGINL_NOSYSERR, cmd_label,
|
|
gettxt(_SGI_DMMX_xpg_mv_dirnoread,
|
|
"Cannot read directory: %s"),path);
|
|
return(1);
|
|
case FTW_NS: /* unknown type, stat failed */
|
|
_sgi_nl_error(SGINL_SYSERR2, cmd_label,
|
|
gettxt(_SGI_DMMX_cannotstat,
|
|
"Cannot stat: %s"),path);
|
|
return(1);
|
|
}
|
|
return(1);
|
|
}
|
|
int
|
|
nftw_rm(const char *path, const struct stat64 *snftw, int nftw_flag, struct FTW *Snftw)
|
|
{
|
|
switch(nftw_flag)
|
|
{
|
|
case FTW_SL: /* symbolic link */
|
|
case FTW_SLN: /* symbolic link that points to nonexistent file */
|
|
case FTW_F: /* file */
|
|
if(unlink(path) < 0) {
|
|
_sgi_nl_error(SGINL_SYSERR2, cmd_label,
|
|
gettxt(_SGI_DMMX_Cannotunlink,
|
|
"Cannot unlink %s"), path);
|
|
}
|
|
return(0);
|
|
|
|
case FTW_DP: /* directory pre visited */
|
|
case FTW_D: /* directory */
|
|
if(rmdir(path) < 0) {
|
|
if(errno == EEXIST)
|
|
errno = ENOTEMPTY;
|
|
_sgi_nl_error(SGINL_SYSERR2, cmd_label,
|
|
gettxt(_SGI_DMMX_xpg_mv_rmdir,
|
|
"Cannot remove directory: %s"),path);
|
|
}
|
|
return(0);
|
|
|
|
case FTW_DNR: /* directory without read permission */
|
|
_sgi_nl_error(SGINL_NOSYSERR, cmd_label,
|
|
gettxt(_SGI_DMMX_xpg_mv_dirnoread,
|
|
"Cannot read directory: %s"),path);
|
|
return(1);
|
|
case FTW_NS: /* unknown type, stat failed */
|
|
_sgi_nl_error(SGINL_SYSERR2, cmd_label,
|
|
gettxt(_SGI_DMMX_cannotstat,
|
|
"Cannot stat: %s"),path);
|
|
return(1);
|
|
}
|
|
return(1);
|
|
}
|
|
|
|
void
|
|
xdev_quit(sig)
|
|
{
|
|
if(xdev_state == 1)
|
|
_sgi_nl_error(SGINL_NOSYSERR, cmd_label,
|
|
gettxt(_SGI_DMMX_xpg_mv_partdest,
|
|
"Warning - incomplete copy to target tree: %s"),xdev_target);
|
|
else
|
|
_sgi_nl_error(SGINL_NOSYSERR, cmd_label,
|
|
gettxt(_SGI_DMMX_xpg_mv_partsrc,
|
|
"Warning - incomplete removal of source tree: %s"),xdev_source);
|
|
exit(1);
|
|
}
|
|
|
|
|
|
|
|
int
|
|
get_fs_type(int fd)
|
|
{
|
|
struct statfs buf;
|
|
|
|
if (fstatfs(fd, &buf, sizeof(buf), 0) < 0) {
|
|
return(-1);
|
|
}
|
|
|
|
if (buf.f_fstyp == fstyp_xfs) return (FS_XFS);
|
|
return(FS_HUH);
|
|
}
|
|
|
|
|
|
/*
|
|
* Attribute cloning code - most of this is here because attr_copy does not
|
|
* let us pick and choose which attributes we want to copy.
|
|
*/
|
|
|
|
attr_multiop_t attr_ops[ATTR_MAX_MULTIOPS];
|
|
|
|
/*
|
|
* Grab attributes specified in attr_ops from source file and write them
|
|
* out on the destination file.
|
|
*/
|
|
|
|
static int
|
|
attr_replicate(int src_fd, int dst_fd, int count)
|
|
{
|
|
int j, k;
|
|
|
|
if (attr_multif(src_fd, attr_ops, count, 0) < 0) {
|
|
return(-1);
|
|
}
|
|
for (k = 0; k < count; k++) {
|
|
if (attr_ops[k].am_error) {
|
|
printf("Error %d getting attribute\n",
|
|
attr_ops[k].am_error);
|
|
break;
|
|
}
|
|
attr_ops[k].am_opcode = ATTR_OP_SET;
|
|
}
|
|
if (attr_multif(dst_fd, attr_ops, k, 0) < 0) {
|
|
perror("on attr_multif set");
|
|
}
|
|
for (j = 0; j < k; j++) {
|
|
if (attr_ops[j].am_error) {
|
|
printf("Error %d setting attribute\n",
|
|
attr_ops[j].am_error);
|
|
return(-1);
|
|
}
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
/*
|
|
* Copy all the attributes specified from src to dst.
|
|
*/
|
|
|
|
static int
|
|
attr_clone_copy(int src_fd, int dst_fd, char *list_buf,
|
|
char *attr_buf, int buf_len, int flags)
|
|
{
|
|
attrlist_t *alist;
|
|
attrlist_ent_t *attr;
|
|
attrlist_cursor_t cursor;
|
|
int space, i, j, k;
|
|
char *ptr;
|
|
|
|
bzero((char *)&cursor, sizeof(cursor));
|
|
do {
|
|
if (attr_listf(src_fd, list_buf, BUFSIZE, flags, &cursor) < 0) {
|
|
perror("on attr_listf");
|
|
return(1);
|
|
}
|
|
|
|
alist = (attrlist_t *)list_buf;
|
|
|
|
space = buf_len;
|
|
ptr = attr_buf;
|
|
for (j = 0, i = 0; i < alist->al_count; i++) {
|
|
attr = ATTR_ENTRY(list_buf, i);
|
|
if (space < attr->a_valuelen) {
|
|
attr_replicate(src_fd, dst_fd, j);
|
|
j = 0;
|
|
space = buf_len;
|
|
ptr = attr_buf;
|
|
}
|
|
attr_ops[j].am_opcode = ATTR_OP_GET;
|
|
attr_ops[j].am_attrname = attr->a_name;
|
|
attr_ops[j].am_attrvalue = ptr;
|
|
attr_ops[j].am_length = (int) attr->a_valuelen;
|
|
attr_ops[j].am_flags = flags;
|
|
attr_ops[j].am_error = 0;
|
|
j++;
|
|
ptr += attr->a_valuelen;
|
|
space -= attr->a_valuelen;
|
|
}
|
|
|
|
if (j) {
|
|
attr_replicate(src_fd, dst_fd, j);
|
|
}
|
|
} while (alist->al_more);
|
|
return(0);
|
|
}
|
|
|
|
|
|
int clone_attribs(
|
|
int in_fd,
|
|
int out_fd)
|
|
{
|
|
char list_buf[BUFSIZE];
|
|
char *attr_buf;
|
|
|
|
if ((get_fs_type(in_fd) != FS_XFS) || (get_fs_type(out_fd) != FS_XFS)) {
|
|
return(0);
|
|
}
|
|
|
|
attr_buf = malloc(ATTR_MAX_VALUELEN * 2);
|
|
attr_clone_copy(in_fd, out_fd, list_buf,
|
|
attr_buf, ATTR_MAX_VALUELEN * 2, 0);
|
|
free(attr_buf);
|
|
return(0);
|
|
}
|
|
|
|
/************
|
|
int calculate_rt_size(int in_fd, int out_fd)
|
|
{
|
|
struct fsxattr fsx_src, fsx_dst;
|
|
int rt_src = 0;
|
|
int rt_dst = 0;
|
|
|
|
if (!fcntl(in_fd, F_FSGETXATTR, &fsx_src) &&
|
|
(fsx_src.fsx_xflags & XFS_XFLAG_REALTIME)) {
|
|
rt_src = fsx_src.fsx_extsize;
|
|
printf("input rt extsize = %d\n", fsx_src.fsx_extsize);
|
|
}
|
|
|
|
if (!fcntl(out_fd, F_FSGETXATTR, &fsx_dst) &&
|
|
(fsx_dst.fsx_xflags & XFS_XFLAG_REALTIME)) {
|
|
rt_dst = fsx_dst.fsx_extsize;
|
|
printf("output rt extsize = %d\n", fsx_dst.fsx_extsize);
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
***********/
|
|
|
|
/*
|
|
* Determine the real time extent size of the XFS file system
|
|
*/
|
|
int
|
|
xfsrtextsize(char *path)
|
|
{
|
|
int fd, rval, rtextsize;
|
|
xfs_fsop_geom_t geo;
|
|
|
|
fd = open( path, O_RDONLY );
|
|
if ( fd < 0 ) {
|
|
Error1(path);
|
|
return -1;
|
|
}
|
|
rval = syssgi( SGI_XFS_FSOPERATIONS,
|
|
fd,
|
|
XFS_FS_GEOMETRY,
|
|
(void *)0, &geo );
|
|
close(fd);
|
|
|
|
rtextsize = geo.rtextsize * geo.blocksize;
|
|
|
|
if ( rval < 0 ) {
|
|
return -1;
|
|
} else {
|
|
return rtextsize;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Read in block map of the input file, coalesce contiguous
|
|
* extents into a single range, keep all holes. Convert from 512 byte
|
|
* blocks to bytes.
|
|
*/
|
|
|
|
#define MAPSIZE 128
|
|
#define OUTMAP_SIZE_INCREMENT MAPSIZE
|
|
|
|
int read_fd_bmap(int fd, int out, struct stat64 *sin, struct stat64 *sout)
|
|
{
|
|
int i, cnt;
|
|
int nextents;
|
|
struct getbmap map[MAPSIZE];
|
|
struct fsxattr fsx;
|
|
|
|
#define BUMP_CNT \
|
|
if (++cnt >= outmap_size) { \
|
|
outmap_size += OUTMAP_SIZE_INCREMENT; \
|
|
outmap = realloc(outmap, outmap_size*sizeof(*outmap)); \
|
|
if (outmap == NULL) { \
|
|
Error1("realloc for XFS extent map"); \
|
|
exit(1); \
|
|
} \
|
|
}
|
|
|
|
/* Initialize the outmap array. It always grows - never shrinks.
|
|
* Left-over memory allocation is saved for the next files.
|
|
*/
|
|
if (outmap_size == 0) {
|
|
outmap_size = OUTMAP_SIZE_INCREMENT; /* Initial size */
|
|
outmap = malloc(outmap_size*sizeof(*outmap));
|
|
if (!outmap) {
|
|
Error1("malloc for XFS extent map");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
outmap[0].bmv_block = 0;
|
|
outmap[0].bmv_offset = 0;
|
|
outmap[0].bmv_length = sin->st_size;
|
|
|
|
/*
|
|
* If a non regular file is involved then forget holes
|
|
*/
|
|
|
|
if (!ISREG(*sin) || !ISREG(*sout))
|
|
return(1);
|
|
|
|
/*
|
|
* If the file length takes less blocks than the file
|
|
* has allocated then we probably do not have holes,
|
|
* don't bother looking for them.
|
|
*/
|
|
|
|
if (BTOBB(sin->st_size) <= sin->st_blocks) {
|
|
return(1);
|
|
}
|
|
|
|
if (!cpy_holes || (get_fs_type(fd) != FS_XFS) ||
|
|
(get_fs_type(out) != FS_XFS)) {
|
|
return(1);
|
|
}
|
|
|
|
|
|
outmap[0].bmv_length = 0;
|
|
|
|
map[0].bmv_offset = 0;
|
|
map[0].bmv_block = 0;
|
|
map[0].bmv_entries = 0;
|
|
map[0].bmv_count = MAPSIZE;
|
|
map[0].bmv_length = -1;
|
|
|
|
cnt = 0;
|
|
|
|
do {
|
|
if (fcntl(fd, F_GETBMAP, map) < 0) {
|
|
Error1("reading XFS extents");
|
|
exit(1);
|
|
}
|
|
|
|
/* Concatenate extents together and replicate holes into
|
|
* the output map.
|
|
*/
|
|
for (i = 0; i < map[0].bmv_entries; i++) {
|
|
if (map[i + 1].bmv_block == -1) {
|
|
BUMP_CNT;
|
|
outmap[cnt] = map[i+1];
|
|
} else if (outmap[cnt].bmv_block == -1) {
|
|
BUMP_CNT;
|
|
outmap[cnt] = map[i+1];
|
|
} else {
|
|
outmap[cnt].bmv_length += map[i + 1].bmv_length;
|
|
}
|
|
}
|
|
} while (map[0].bmv_entries == (MAPSIZE-1));
|
|
|
|
for (i = 0; i <= cnt; i++) {
|
|
outmap[i].bmv_offset = BBTOB(outmap[i].bmv_offset);
|
|
outmap[i].bmv_length = BBTOB(outmap[i].bmv_length);
|
|
}
|
|
|
|
outmap[cnt].bmv_length = sin->st_size - outmap[cnt].bmv_offset;
|
|
|
|
return(cnt+1);
|
|
}
|