14887Schin /***********************************************************************
24887Schin * *
34887Schin * This software is part of the ast package *
4*12068SRoger.Faulkner@Oracle.COM * Copyright (c) 1992-2010 AT&T Intellectual Property *
54887Schin * and is licensed under the *
64887Schin * Common Public License, Version 1.0 *
78462SApril.Chin@Sun.COM * by AT&T Intellectual Property *
84887Schin * *
94887Schin * A copy of the License is available at *
104887Schin * http://www.opensource.org/licenses/cpl1.0.txt *
114887Schin * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
124887Schin * *
134887Schin * Information and Software Systems Research *
144887Schin * AT&T Research *
154887Schin * Florham Park NJ *
164887Schin * *
174887Schin * Glenn Fowler <gsf@research.att.com> *
184887Schin * David Korn <dgk@research.att.com> *
194887Schin * *
204887Schin ***********************************************************************/
214887Schin #pragma prototyped
224887Schin /*
234887Schin * Glenn Fowler
244887Schin * AT&T Research
254887Schin *
264887Schin * cp/ln/mv -- copy/link/move files
274887Schin */
284887Schin
294887Schin static const char usage_head[] =
30*12068SRoger.Faulkner@Oracle.COM "[-?@(#)$Id: cp (AT&T Research) 2010-01-20 $\n]"
314887Schin USAGE_LICENSE
324887Schin ;
334887Schin
344887Schin static const char usage_cp[] =
354887Schin "[+NAME?cp - copy files]"
364887Schin "[+DESCRIPTION?If the last argument names an existing directory, \bcp\b"
374887Schin " copies each \afile\a into a file with the same name in that"
384887Schin " directory. Otherwise, if only two files are given, \bcp\b copies"
394887Schin " the first onto the second. It is an error if the last argument is"
404887Schin " not a directory and more than two files are given. By default"
414887Schin " directories are not copied.]"
424887Schin
434887Schin "[a:archive?Preserve as much as possible of the structure and attributes of"
444887Schin " the original files in the copy. Equivalent to \b--physical\b"
454887Schin " \b--preserve\b \b--recursive\b.]"
464887Schin "[p:preserve?Preserve file owner, group, permissions and timestamps.]"
474887Schin "[h:hierarchy|parents?Form the name of each destination file by appending"
484887Schin " to the target directory a slash and the specified source file name."
494887Schin " The last argument must be an existing directory. Missing destination"
504887Schin " directories are created.]"
514887Schin "[H:metaphysical?Follow command argument symbolic links, otherwise don't"
524887Schin " follow.]"
534887Schin "[l:link?Make hard links to destination files instead of copies.]"
544887Schin "[L:logical|dereference?Follow symbolic links and copy the files"
554887Schin " they point to.]"
564887Schin "[P|d:physical|nodereference?Don't follow symbolic links; copy symbolic"
574887Schin " rather than the files they point to.]"
584887Schin ;
594887Schin
604887Schin static const char usage_ln[] =
614887Schin "[+NAME?ln - link files]"
624887Schin "[+DESCRIPTION?If the last argument names an existing directory, \bln\b"
634887Schin " links each \afile\a into a file with the same name in that"
644887Schin " directory. Otherwise, if only two files are given, \bln\b links"
654887Schin " the first onto the second. It is an error if the last argument is"
664887Schin " not a directory and more than two files are given. By default"
674887Schin " directories are not linked.]"
684887Schin ;
694887Schin
704887Schin static const char usage_mv[] =
714887Schin "[+NAME?mv - rename files]"
724887Schin "[+DESCRIPTION?If the last argument names an existing directory, \bmv\b"
734887Schin " renames each \afile\a into a file with the same name in that"
744887Schin " directory. Otherwise, if only two files are given, \bmv\b renames"
754887Schin " the first onto the second. It is an error if the last argument is"
764887Schin " not a directory and more than two files are given. If a source and"
774887Schin " destination file reside on different filesystems then \bmv\b copies"
784887Schin " the file contents to the destination and then deletes the source"
794887Schin " file.]"
804887Schin ;
814887Schin
824887Schin static const char usage_tail[] =
834887Schin "[f:force?Replace existing destination files.]"
844887Schin "[i:interactive|prompt?Prompt whether to replace existing destination files."
854887Schin " An affirmative response (\by\b or \bY\b) replaces the file, a quit"
864887Schin " response (\bq\b or \bQ\b) exits immediately, and all other"
874887Schin " responses skip the file.]"
884887Schin "[r|R:recursive?Operate on the contents of directories recursively.]"
894887Schin "[s:symlink|symbolic-link?Make symbolic links to destination files.]"
904887Schin "[u:update?Replace a destination file only if its modification time is older"
914887Schin " than the corresponding source file modification time.]"
924887Schin "[v:verbose?Print the name of each file before operating on it.]"
934887Schin "[b:backup?Make backups of files that are about to be replaced. See"
944887Schin " \b--suffix\b and \b--version-control\b for more information.]"
954887Schin "[F:fsync|sync?\bfsync\b(2) each file after it is copied.]"
964887Schin "[S:backup-suffix|suffix?A backup file is made by renaming the file to the"
974887Schin " same name with the backup suffix appended. The backup suffix is"
984887Schin " determined in this order: this option, the \bSIMPLE_BACKUP_SUFFIX\b,"
994887Schin " environment variable, or the default value \b~\b.]:[suffix]"
1004887Schin "[V:backup-type|version-control?The backup type is determined in this order:"
1014887Schin " this option, the \bVERSION_CONTROL\b environment variable, or the"
1024887Schin " default value \bexisting\b. \atype\a may be one of:]:[type]{"
1034887Schin " [+numbered|t?Always make numbered backups. The numbered backup"
1044887Schin " suffix is \b.\aSNS\a, where \aS\a is the"
1054887Schin " \bbackup-suffix\b and \aN\a is the version number,"
1064887Schin " starting at 1, incremented with each version.]"
1074887Schin " [+existing|nil?Make numbered backups of files that already"
1084887Schin " have them, otherwise simple backups.]"
1094887Schin " [+simple|never?Always make simple backups.]"
1104887Schin "}"
1114887Schin "[x|X|l:xdev|local|mount|one-file-system?Do not descend into directories in"
1124887Schin " different filesystems than their parents.]"
1134887Schin
1144887Schin "\n"
1154887Schin "\nsource destination\n"
1164887Schin "file ... directory\n"
1174887Schin "\n"
1184887Schin
1194887Schin "[+SEE ALSO?\bpax\b(1), \bfsync\b(2), \brename\b(2), \bunlink\b(2),"
1204887Schin " \bremove\b(3)]"
1214887Schin ;
1224887Schin
1234887Schin #include <cmd.h>
1244887Schin #include <ls.h>
1254887Schin #include <times.h>
126*12068SRoger.Faulkner@Oracle.COM #include <fts_fix.h>
1274887Schin #include <fs3d.h>
1284887Schin #include <hashkey.h>
1294887Schin #include <stk.h>
1304887Schin #include <tmx.h>
1314887Schin
1324887Schin #define PATH_CHUNK 256
1334887Schin
1344887Schin #define CP 1
1354887Schin #define LN 2
1364887Schin #define MV 3
1374887Schin
1384887Schin #define BAK_replace 0 /* no backup -- just replace */
1394887Schin #define BAK_existing 1 /* number if already else simple*/
1404887Schin #define BAK_number 2 /* append .suffix number suffix */
1414887Schin #define BAK_simple 3 /* append suffix */
1424887Schin
1434887Schin typedef struct State_s /* program state */
1444887Schin {
14510898Sroland.mainz@nrubsig.org void* context; /* builtin context */
1464887Schin int backup; /* BAK_* type */
1474887Schin int directory; /* destination is directory */
1484887Schin int flags; /* FTS_* flags */
1494887Schin int force; /* force approval */
1504887Schin int fs3d; /* 3d fs enabled */
1514887Schin int hierarchy; /* preserve hierarchy */
1524887Schin int interactive; /* prompt for approval */
1534887Schin int missmode; /* default missing dir mode */
1544887Schin int official; /* move to next view */
1554887Schin int op; /* {CP,LN,MV} */
1564887Schin int perm; /* permissions to preserve */
1574887Schin int postsiz; /* state.path post index */
1584887Schin int presiz; /* state.path pre index */
1594887Schin int preserve; /* preserve { id mode time } */
1604887Schin int recursive; /* subtrees too */
1614887Schin int suflen; /* strlen(state.suffix) */
1624887Schin int sync; /* fsync() each file after copy */
1634887Schin int uid; /* caller uid */
1644887Schin int update; /* replace only if newer */
1654887Schin int verbose; /* list each file before op */
1668462SApril.Chin@Sun.COM int wflags; /* open() for write flags */
1674887Schin
1684887Schin int (*link)(const char*, const char*); /* link */
1694887Schin int (*stat)(const char*, struct stat*); /* stat */
1704887Schin
1718462SApril.Chin@Sun.COM #define INITSTATE pathsiz /* (re)init state before this */
1728462SApril.Chin@Sun.COM int pathsiz; /* state.path buffer size */
1738462SApril.Chin@Sun.COM
1748462SApril.Chin@Sun.COM
1754887Schin char* path; /* to pathname buffer */
1764887Schin char* opname; /* state.op message string */
1774887Schin char* suffix; /* backup suffix */
1784887Schin
1794887Schin Sfio_t* tmp; /* tmp string stream */
1804887Schin
1814887Schin char text[PATH_MAX]; /* link text buffer */
1824887Schin } State_t;
1834887Schin
1844887Schin static const char dot[2] = { '.' };
1854887Schin
1864887Schin /*
1874887Schin * preserve support
1884887Schin */
1894887Schin
1904887Schin static void
preserve(State_t * state,const char * path,struct stat * ns,struct stat * os)1914887Schin preserve(State_t* state, const char* path, struct stat* ns, struct stat* os)
1924887Schin {
1934887Schin int n;
1944887Schin
1954887Schin if (tmxtouch(path, tmxgetatime(os), tmxgetmtime(os), TMX_NOTIME, 0))
1964887Schin error(ERROR_SYSTEM|2, "%s: cannot reset access and modify times", path);
1974887Schin n = ((ns->st_uid != os->st_uid) << 1) | (ns->st_gid != os->st_gid);
1984887Schin if (n && chown(state->path, os->st_uid, os->st_gid))
1994887Schin switch (n)
2004887Schin {
2014887Schin case 01:
2024887Schin error(ERROR_SYSTEM|2, "%s: cannot reset group to %s", path, fmtgid(os->st_gid));
2034887Schin break;
2044887Schin case 02:
2054887Schin error(ERROR_SYSTEM|2, "%s: cannot reset owner to %s", path, fmtuid(os->st_uid));
2064887Schin break;
2074887Schin case 03:
2084887Schin error(ERROR_SYSTEM|2, "%s: cannot reset owner to %s and group to %s", path, fmtuid(os->st_uid), fmtgid(os->st_gid));
2094887Schin break;
2104887Schin }
2114887Schin }
2124887Schin
2134887Schin /*
2144887Schin * visit a single file and state.op to the destination
2154887Schin */
2164887Schin
2174887Schin static int
visit(State_t * state,register FTSENT * ent)2184887Schin visit(State_t* state, register FTSENT* ent)
2194887Schin {
2204887Schin register char* base;
2214887Schin register int n;
2224887Schin register int len;
2234887Schin int rm;
2244887Schin int rfd;
2254887Schin int wfd;
2264887Schin int m;
2274887Schin int v;
2284887Schin char* s;
2294887Schin char* e;
2304887Schin char* protection;
2314887Schin Sfio_t* ip;
2324887Schin Sfio_t* op;
2334887Schin FTS* fts;
2344887Schin FTSENT* sub;
2354887Schin struct stat st;
2364887Schin
2374887Schin if (ent->fts_info == FTS_DC)
2384887Schin {
2394887Schin error(2, "%s: directory causes cycle", ent->fts_path);
2404887Schin fts_set(NiL, ent, FTS_SKIP);
2414887Schin return 0;
2424887Schin }
2434887Schin if (ent->fts_level == 0)
2444887Schin {
2454887Schin base = ent->fts_name;
2464887Schin len = ent->fts_namelen;
2474887Schin if (state->hierarchy)
2484887Schin state->presiz = -1;
2494887Schin else
2504887Schin {
2514887Schin state->presiz = ent->fts_pathlen;
2524887Schin while (*base == '.' && *(base + 1) == '/')
2534887Schin for (base += 2; *base == '/'; base++);
2544887Schin if (*base == '.' && !*(base + 1))
2554887Schin state->presiz--;
2564887Schin else if (*base)
2574887Schin state->presiz -= base - ent->fts_name;
2584887Schin base = ent->fts_name + len;
2594887Schin while (base > ent->fts_name && *(base - 1) == '/')
2604887Schin base--;
2614887Schin while (base > ent->fts_name && *(base - 1) != '/')
2624887Schin base--;
2634887Schin len -= base - ent->fts_name;
2644887Schin if (state->directory)
2654887Schin state->presiz -= len + 1;
2664887Schin }
2674887Schin }
2684887Schin else
2694887Schin {
2704887Schin base = ent->fts_path + state->presiz + 1;
2714887Schin len = ent->fts_pathlen - state->presiz - 1;
2724887Schin }
2734887Schin len++;
2744887Schin if (state->directory)
2754887Schin {
2764887Schin if ((state->postsiz + len) > state->pathsiz && !(state->path = newof(state->path, char, state->pathsiz = roundof(state->postsiz + len, PATH_CHUNK), 0)))
277*12068SRoger.Faulkner@Oracle.COM error(ERROR_SYSTEM|3, "out of space");
2784887Schin if (state->hierarchy && ent->fts_level == 0 && strchr(base, '/'))
2794887Schin {
2804887Schin s = state->path + state->postsiz;
2814887Schin memcpy(s, base, len);
2824887Schin while (e = strchr(s, '/'))
2834887Schin {
2844887Schin *e = 0;
2854887Schin if (access(state->path, F_OK))
2864887Schin {
2874887Schin st.st_mode = state->missmode;
2884887Schin if (s = strrchr(s, '/'))
2894887Schin {
2904887Schin *s = 0;
2914887Schin stat(state->path, &st);
2924887Schin *s = '/';
2934887Schin }
2944887Schin if (mkdir(state->path, st.st_mode & S_IPERM))
2954887Schin {
2964887Schin error(ERROR_SYSTEM|2, "%s: cannot create directory -- %s ignored", state->path, ent->fts_path);
2974887Schin fts_set(NiL, ent, FTS_SKIP);
2984887Schin return 0;
2994887Schin }
3004887Schin }
3014887Schin *e++ = '/';
3024887Schin s = e;
3034887Schin }
3044887Schin }
3054887Schin }
3064887Schin switch (ent->fts_info)
3074887Schin {
3084887Schin case FTS_DP:
3094887Schin if (state->preserve && state->op != LN || ent->fts_level > 0 && (ent->fts_statp->st_mode & S_IRWXU) != S_IRWXU)
3104887Schin {
3114887Schin if (len && ent->fts_level > 0)
3124887Schin memcpy(state->path + state->postsiz, base, len);
3134887Schin else
3144887Schin state->path[state->postsiz] = 0;
3154887Schin if (stat(state->path, &st))
3164887Schin error(ERROR_SYSTEM|2, "%s: cannot stat", state->path);
3174887Schin else
3184887Schin {
3194887Schin if ((ent->fts_statp->st_mode & S_IPERM) != (st.st_mode & S_IPERM) && chmod(state->path, ent->fts_statp->st_mode & S_IPERM))
3204887Schin error(ERROR_SYSTEM|2, "%s: cannot reset directory mode to %s", state->path, fmtmode(st.st_mode & S_IPERM, 0) + 1);
3214887Schin if (state->preserve)
3224887Schin preserve(state, state->path, &st, ent->fts_statp);
3234887Schin }
3244887Schin }
3254887Schin return 0;
3264887Schin case FTS_DNR:
3274887Schin case FTS_DNX:
3284887Schin case FTS_D:
3294887Schin if (!state->recursive)
3304887Schin {
3314887Schin fts_set(NiL, ent, FTS_SKIP);
3324887Schin if (state->op == CP)
3334887Schin error(1, "%s: directory -- copying as plain file", ent->fts_path);
3344887Schin else if (state->link == link && !state->force)
3354887Schin {
3364887Schin error(2, "%s: cannot link directory", ent->fts_path);
3374887Schin return 0;
3384887Schin }
3394887Schin }
3404887Schin else switch (ent->fts_info)
3414887Schin {
3424887Schin case FTS_DNR:
3434887Schin error(2, "%s: cannot read directory", ent->fts_path);
3444887Schin return 0;
3454887Schin case FTS_DNX:
3464887Schin error(2, "%s: cannot search directory", ent->fts_path);
3474887Schin fts_set(NiL, ent, FTS_SKIP);
3484887Schin
3494887Schin /*FALLTHROUGH*/
3504887Schin case FTS_D:
3514887Schin if (state->directory)
3524887Schin memcpy(state->path + state->postsiz, base, len);
3534887Schin if (!(*state->stat)(state->path, &st))
3544887Schin {
3554887Schin if (!S_ISDIR(st.st_mode))
3564887Schin {
3574887Schin error(2, "%s: not a directory -- %s ignored", state->path, ent->fts_path);
3584887Schin return 0;
3594887Schin }
3604887Schin }
3614887Schin else if (mkdir(state->path, (ent->fts_statp->st_mode & S_IPERM)|(ent->fts_info == FTS_D ? S_IRWXU : 0)))
3624887Schin {
3634887Schin error(ERROR_SYSTEM|2, "%s: cannot create directory -- %s ignored", state->path, ent->fts_path);
3644887Schin fts_set(NiL, ent, FTS_SKIP);
3654887Schin }
3664887Schin if (!state->directory)
3674887Schin {
3684887Schin state->directory = 1;
3694887Schin state->path[state->postsiz++] = '/';
3704887Schin state->presiz--;
3714887Schin }
3724887Schin return 0;
3734887Schin }
3744887Schin break;
3754887Schin case FTS_ERR:
3764887Schin case FTS_NS:
3774887Schin case FTS_SLNONE:
3784887Schin if (state->link != pathsetlink)
3794887Schin {
3804887Schin error(2, "%s: not found", ent->fts_path);
3814887Schin return 0;
3824887Schin }
3834887Schin break;
3844887Schin #if 0
3854887Schin case FTS_SL:
3864887Schin if (state->op == CP)
3874887Schin {
3884887Schin error(2, "%s: cannot copy non-terminal symbolic link", ent->fts_path);
3894887Schin return 0;
3904887Schin }
3914887Schin break;
3924887Schin #endif
3934887Schin }
3944887Schin if (state->directory)
3954887Schin memcpy(state->path + state->postsiz, base, len);
3964887Schin if ((*state->stat)(state->path, &st))
3974887Schin st.st_mode = 0;
3984887Schin else if (state->update && !S_ISDIR(st.st_mode) && (unsigned long)ent->fts_statp->st_mtime < (unsigned long)st.st_mtime)
3994887Schin {
4004887Schin fts_set(NiL, ent, FTS_SKIP);
4014887Schin return 0;
4024887Schin }
4034887Schin else if (!state->fs3d || !iview(&st))
4044887Schin {
4054887Schin /*
4064887Schin * target is in top 3d view
4074887Schin */
4084887Schin
4094887Schin if (st.st_dev == ent->fts_statp->st_dev && st.st_ino == ent->fts_statp->st_ino)
4104887Schin {
4114887Schin if (state->op == MV)
4124887Schin {
4134887Schin /*
4144887Schin * let rename() handle it
4154887Schin */
4164887Schin
4174887Schin if (state->verbose)
4184887Schin sfputr(sfstdout, state->path, '\n');
4194887Schin goto operate;
4204887Schin }
4214887Schin if (!state->official)
4224887Schin error(2, "%s: identical to %s", state->path, ent->fts_path);
4234887Schin return 0;
4244887Schin }
4254887Schin if (S_ISDIR(st.st_mode))
4264887Schin {
4274887Schin error(2, "%s: cannot %s existing directory", state->path, state->opname);
4284887Schin return 0;
4294887Schin }
4304887Schin if (state->verbose)
4314887Schin sfputr(sfstdout, state->path, '\n');
4324887Schin rm = state->op == LN || ent->fts_info == FTS_SL;
4334887Schin if (!rm || !state->force)
4344887Schin {
4354887Schin if ((n = open(state->path, O_RDWR|O_BINARY)) >= 0)
4364887Schin {
4374887Schin close(n);
4384887Schin if (state->force)
4394887Schin /* ok */;
4404887Schin else if (state->interactive)
4414887Schin {
44210898Sroland.mainz@nrubsig.org if (astquery(-1, "%s %s? ", state->opname, state->path) < 0 || sh_checksig(state->context))
4434887Schin return 0;
4444887Schin }
4454887Schin else if (state->op == LN)
4464887Schin {
4474887Schin error(2, "%s: cannot %s existing file", state->path, state->opname);
4484887Schin return 0;
4494887Schin }
4504887Schin }
4514887Schin else if (state->force)
4524887Schin rm = 1;
4534887Schin else
4544887Schin {
4554887Schin protection =
4564887Schin #ifdef ETXTBSY
4574887Schin errno == ETXTBSY ? "``running program''" :
4584887Schin #endif
4594887Schin st.st_uid != state->uid ? "``not owner''" :
4604887Schin fmtmode(st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO), 0) + 1;
4614887Schin if (state->interactive)
4624887Schin {
46310898Sroland.mainz@nrubsig.org if (astquery(-1, "override protection %s for %s? ", protection, state->path) < 0 || sh_checksig(state->context))
4644887Schin return 0;
4654887Schin rm = 1;
4664887Schin }
4674887Schin else if (!rm)
4684887Schin {
4694887Schin error(2, "%s: cannot %s %s protection", state->path, state->opname, protection);
4704887Schin return 0;
4714887Schin }
4724887Schin }
4734887Schin }
4744887Schin switch (state->backup)
4754887Schin {
4764887Schin case BAK_existing:
4774887Schin case BAK_number:
4784887Schin v = 0;
4794887Schin if (s = strrchr(state->path, '/'))
4804887Schin {
4814887Schin e = state->path;
4824887Schin *s++ = 0;
4834887Schin }
4844887Schin else
4854887Schin {
4864887Schin e = (char*)dot;
4874887Schin s = state->path;
4884887Schin }
4894887Schin n = strlen(s);
4904887Schin if (fts = fts_open((char**)e, FTS_NOCHDIR|FTS_ONEPATH|FTS_PHYSICAL|FTS_NOPOSTORDER|FTS_NOSTAT|FTS_NOSEEDOTDIR, NiL))
4914887Schin {
4924887Schin while (sub = fts_read(fts))
4934887Schin {
4944887Schin if (strneq(s, sub->fts_name, n) && sub->fts_name[n] == '.' && strneq(sub->fts_name + n + 1, state->suffix, state->suflen) && (m = strtol(sub->fts_name + n + state->suflen + 1, &e, 10)) && streq(e, state->suffix) && m > v)
4954887Schin v = m;
4964887Schin if (sub->fts_level)
4974887Schin fts_set(NiL, sub, FTS_SKIP);
4984887Schin }
4994887Schin fts_close(fts);
5004887Schin }
5014887Schin if (s != state->path)
5024887Schin *--s = '/';
5034887Schin if (v || state->backup == BAK_number)
5044887Schin {
5054887Schin sfprintf(state->tmp, "%s.%s%d%s", state->path, state->suffix, v + 1, state->suffix);
5064887Schin goto backup;
5074887Schin }
5084887Schin /*FALLTHROUGH*/
5094887Schin case BAK_simple:
5104887Schin sfprintf(state->tmp, "%s%s", state->path, state->suffix);
5114887Schin backup:
5124887Schin if (!(s = sfstruse(state->tmp)))
5134887Schin error(ERROR_SYSTEM|3, "%s: out of space", state->path);
5144887Schin if (rename(state->path, s))
5154887Schin {
5164887Schin error(ERROR_SYSTEM|2, "%s: cannot backup to %s", state->path, s);
5174887Schin return 0;
5184887Schin }
5194887Schin break;
5204887Schin default:
5214887Schin if (rm && remove(state->path))
5224887Schin {
5234887Schin error(ERROR_SYSTEM|2, "%s: cannot remove", state->path);
5244887Schin return 0;
5254887Schin }
5264887Schin break;
5274887Schin }
5284887Schin }
5294887Schin operate:
5304887Schin switch (state->op)
5314887Schin {
5324887Schin case MV:
5334887Schin for (;;)
5344887Schin {
5354887Schin if (!rename(ent->fts_path, state->path))
5364887Schin return 0;
5374887Schin if (errno == ENOENT)
5384887Schin rm = 1;
5394887Schin else if (!rm && st.st_mode && !remove(state->path))
5404887Schin {
5414887Schin rm = 1;
5424887Schin continue;
5434887Schin }
5444887Schin if (errno != EXDEV && (rm || S_ISDIR(ent->fts_statp->st_mode)))
5454887Schin {
5464887Schin error(ERROR_SYSTEM|2, "%s: cannot rename to %s", ent->fts_path, state->path);
5474887Schin return 0;
5484887Schin }
5494887Schin else
5504887Schin break;
5514887Schin }
5524887Schin /*FALLTHROUGH*/
5534887Schin case CP:
5544887Schin if (S_ISLNK(ent->fts_statp->st_mode))
5554887Schin {
5564887Schin if ((n = pathgetlink(ent->fts_path, state->text, sizeof(state->text) - 1)) < 0)
5574887Schin {
5584887Schin error(ERROR_SYSTEM|2, "%s: cannot read symbolic link text", ent->fts_path);
5594887Schin return 0;
5604887Schin }
5614887Schin state->text[n] = 0;
5624887Schin if (pathsetlink(state->text, state->path))
5634887Schin {
5644887Schin error(ERROR_SYSTEM|2, "%s: cannot copy symbolic link to %s", ent->fts_path, state->path);
5654887Schin return 0;
5664887Schin }
5674887Schin }
5684887Schin else if (state->op == CP || S_ISREG(ent->fts_statp->st_mode) || S_ISDIR(ent->fts_statp->st_mode))
5694887Schin {
5704887Schin if (ent->fts_statp->st_size > 0 && (rfd = open(ent->fts_path, O_RDONLY|O_BINARY)) < 0)
5714887Schin {
5724887Schin error(ERROR_SYSTEM|2, "%s: cannot read", ent->fts_path);
5734887Schin return 0;
5744887Schin }
5758462SApril.Chin@Sun.COM else if ((wfd = open(state->path, st.st_mode ? (state->wflags & ~O_EXCL) : state->wflags, ent->fts_statp->st_mode & state->perm)) < 0)
5764887Schin {
5774887Schin error(ERROR_SYSTEM|2, "%s: cannot write", state->path);
5784887Schin if (ent->fts_statp->st_size > 0)
5794887Schin close(rfd);
5804887Schin return 0;
5814887Schin }
5824887Schin else if (ent->fts_statp->st_size > 0)
5834887Schin {
5844887Schin if (!(ip = sfnew(NiL, NiL, SF_UNBOUND, rfd, SF_READ)))
5854887Schin {
5864887Schin error(ERROR_SYSTEM|2, "%s: %s read stream error", ent->fts_path, state->path);
5874887Schin close(rfd);
5884887Schin close(wfd);
5894887Schin }
5904887Schin else
5914887Schin {
5924887Schin n = 0;
5934887Schin if (!(op = sfnew(NiL, NiL, SF_UNBOUND, wfd, SF_WRITE)))
5944887Schin {
5954887Schin error(ERROR_SYSTEM|2, "%s: %s write stream error", ent->fts_path, state->path);
5964887Schin close(wfd);
5974887Schin sfclose(ip);
5984887Schin }
5994887Schin else
6004887Schin {
6014887Schin if (sfmove(ip, op, (Sfoff_t)SF_UNBOUND, -1) < 0)
6024887Schin n |= 3;
6034887Schin if (!sfeof(ip))
6044887Schin n |= 1;
6054887Schin if (sfsync(op) || state->sync && fsync(wfd) || sfclose(op))
6064887Schin n |= 2;
6074887Schin if (sfclose(ip))
6084887Schin n |= 1;
6094887Schin if (n)
6104887Schin error(ERROR_SYSTEM|2, "%s: %s %s error", ent->fts_path, state->path, n == 1 ? ERROR_translate(0, 0, 0, "read") : n == 2 ? ERROR_translate(0, 0, 0, "write") : ERROR_translate(0, 0, 0, "io"));
6114887Schin }
6124887Schin }
6134887Schin }
6144887Schin else
6154887Schin close(wfd);
6164887Schin }
6174887Schin else if (S_ISBLK(ent->fts_statp->st_mode) || S_ISCHR(ent->fts_statp->st_mode) || S_ISFIFO(ent->fts_statp->st_mode))
6184887Schin {
6194887Schin if (mknod(state->path, ent->fts_statp->st_mode, idevice(ent->fts_statp)))
6204887Schin {
6214887Schin error(ERROR_SYSTEM|2, "%s: cannot copy special file to %s", ent->fts_path, state->path);
6224887Schin return 0;
6234887Schin }
6244887Schin }
6254887Schin else
6264887Schin {
6274887Schin error(2, "%s: cannot copy -- unknown file type 0%o", ent->fts_path, S_ITYPE(ent->fts_statp->st_mode));
6284887Schin return 0;
6294887Schin }
6304887Schin if (state->preserve)
6314887Schin {
6324887Schin if (ent->fts_info != FTS_SL)
6334887Schin {
6344887Schin if (stat(state->path, &st))
6354887Schin error(ERROR_SYSTEM|2, "%s: cannot stat", state->path);
6364887Schin else
6374887Schin {
6384887Schin if ((ent->fts_statp->st_mode & state->perm) != (st.st_mode & state->perm) && chmod(state->path, ent->fts_statp->st_mode & state->perm))
6394887Schin error(ERROR_SYSTEM|2, "%s: cannot reset mode to %s", state->path, fmtmode(st.st_mode & state->perm, 0) + 1);
6404887Schin preserve(state, state->path, &st, ent->fts_statp);
6414887Schin }
6424887Schin }
6434887Schin if (state->op == MV && remove(ent->fts_path))
6444887Schin error(ERROR_SYSTEM|1, "%s: cannot remove", ent->fts_path);
6454887Schin }
6464887Schin break;
6474887Schin case LN:
6484887Schin if ((*state->link)(ent->fts_path, state->path))
6494887Schin error(ERROR_SYSTEM|2, "%s: cannot link to %s", ent->fts_path, state->path);
6504887Schin break;
6514887Schin }
6524887Schin return 0;
6534887Schin }
6544887Schin
6554887Schin int
b_cp(int argc,register char ** argv,void * context)6564887Schin b_cp(int argc, register char** argv, void* context)
6574887Schin {
6584887Schin register char* file;
6594887Schin register char* s;
6604887Schin char** v;
6614887Schin char* backup_type;
6624887Schin FTS* fts;
66310898Sroland.mainz@nrubsig.org FTSENT* ent;
6644887Schin const char* usage;
6654887Schin int path_resolve;
6664887Schin int standard;
6674887Schin struct stat st;
6688462SApril.Chin@Sun.COM State_t* state;
6698462SApril.Chin@Sun.COM Shbltin_t* sh;
670*12068SRoger.Faulkner@Oracle.COM void* cleanup = context;
6714887Schin
6724887Schin cmdinit(argc, argv, context, ERROR_CATALOG, ERROR_NOTIFY);
6738462SApril.Chin@Sun.COM if (!(sh = CMD_CONTEXT(context)) || !(state = (State_t*)sh->ptr))
6748462SApril.Chin@Sun.COM {
6758462SApril.Chin@Sun.COM if (!(state = newof(0, State_t, 1, 0)))
6768462SApril.Chin@Sun.COM error(ERROR_SYSTEM|3, "out of space");
6778462SApril.Chin@Sun.COM if (sh)
6788462SApril.Chin@Sun.COM sh->ptr = state;
6798462SApril.Chin@Sun.COM }
6808462SApril.Chin@Sun.COM else
6818462SApril.Chin@Sun.COM memset(state, 0, offsetof(State_t, INITSTATE));
68210898Sroland.mainz@nrubsig.org state->context = context;
6838462SApril.Chin@Sun.COM state->presiz = -1;
6844887Schin backup_type = 0;
6858462SApril.Chin@Sun.COM state->flags = FTS_NOCHDIR|FTS_NOSEEDOTDIR;
6868462SApril.Chin@Sun.COM state->uid = geteuid();
6878462SApril.Chin@Sun.COM state->wflags = O_WRONLY|O_CREAT|O_TRUNC|O_BINARY;
6888462SApril.Chin@Sun.COM if (!state->tmp && !(state->tmp = sfstropen()))
6894887Schin error(ERROR_SYSTEM|3, "out of space [tmp string]");
6908462SApril.Chin@Sun.COM sfputr(state->tmp, usage_head, -1);
6914887Schin standard = !strcmp(astconf("CONFORMANCE", NiL, NiL), "standard");
6924887Schin switch (error_info.id[0])
6934887Schin {
6944887Schin case 'c':
6954887Schin case 'C':
6968462SApril.Chin@Sun.COM sfputr(state->tmp, usage_cp, -1);
6978462SApril.Chin@Sun.COM state->op = CP;
6988462SApril.Chin@Sun.COM state->stat = stat;
6994887Schin path_resolve = -1;
7004887Schin break;
7014887Schin case 'l':
7024887Schin case 'L':
7038462SApril.Chin@Sun.COM sfputr(state->tmp, usage_ln, -1);
7048462SApril.Chin@Sun.COM state->op = LN;
7058462SApril.Chin@Sun.COM state->flags |= FTS_PHYSICAL;
7068462SApril.Chin@Sun.COM state->link = link;
7078462SApril.Chin@Sun.COM state->stat = lstat;
7084887Schin path_resolve = 1;
7094887Schin break;
7104887Schin case 'm':
7114887Schin case 'M':
7128462SApril.Chin@Sun.COM sfputr(state->tmp, usage_mv, -1);
7138462SApril.Chin@Sun.COM state->op = MV;
7148462SApril.Chin@Sun.COM state->flags |= FTS_PHYSICAL;
7158462SApril.Chin@Sun.COM state->preserve = 1;
7168462SApril.Chin@Sun.COM state->stat = lstat;
7174887Schin path_resolve = 1;
7184887Schin break;
7194887Schin default:
7204887Schin error(3, "not implemented");
7214887Schin break;
7224887Schin }
7238462SApril.Chin@Sun.COM sfputr(state->tmp, usage_tail, -1);
7248462SApril.Chin@Sun.COM if (!(usage = sfstruse(state->tmp)))
7258462SApril.Chin@Sun.COM error(ERROR_SYSTEM|3, "%s: out of space", state->path);
7268462SApril.Chin@Sun.COM state->opname = state->op == CP ? ERROR_translate(0, 0, 0, "overwrite") : ERROR_translate(0, 0, 0, "replace");
7274887Schin for (;;)
7284887Schin {
7294887Schin switch (optget(argv, usage))
7304887Schin {
7314887Schin case 'a':
7328462SApril.Chin@Sun.COM state->flags |= FTS_PHYSICAL;
7338462SApril.Chin@Sun.COM state->preserve = 1;
7348462SApril.Chin@Sun.COM state->recursive = 1;
7354887Schin path_resolve = 1;
7364887Schin continue;
7374887Schin case 'b':
7388462SApril.Chin@Sun.COM state->backup = 1;
7394887Schin continue;
7404887Schin case 'f':
7418462SApril.Chin@Sun.COM state->force = 1;
7428462SApril.Chin@Sun.COM if (state->op != CP || !standard)
7438462SApril.Chin@Sun.COM state->interactive = 0;
7444887Schin continue;
7454887Schin case 'h':
7468462SApril.Chin@Sun.COM state->hierarchy = 1;
7474887Schin continue;
7484887Schin case 'i':
7498462SApril.Chin@Sun.COM state->interactive = 1;
7508462SApril.Chin@Sun.COM if (state->op != CP || !standard)
7518462SApril.Chin@Sun.COM state->force = 0;
7524887Schin continue;
7534887Schin case 'l':
7548462SApril.Chin@Sun.COM state->op = LN;
7558462SApril.Chin@Sun.COM state->link = link;
7568462SApril.Chin@Sun.COM state->stat = lstat;
7574887Schin continue;
7584887Schin case 'p':
7598462SApril.Chin@Sun.COM state->preserve = 1;
7604887Schin continue;
7614887Schin case 'r':
7628462SApril.Chin@Sun.COM state->recursive = 1;
7634887Schin if (path_resolve < 0)
7644887Schin path_resolve = 0;
7654887Schin continue;
7664887Schin case 's':
7678462SApril.Chin@Sun.COM state->op = LN;
7688462SApril.Chin@Sun.COM state->link = pathsetlink;
7698462SApril.Chin@Sun.COM state->stat = lstat;
7704887Schin continue;
7714887Schin case 'u':
7728462SApril.Chin@Sun.COM state->update = 1;
7734887Schin continue;
7744887Schin case 'v':
7758462SApril.Chin@Sun.COM state->verbose = 1;
7764887Schin continue;
7774887Schin case 'x':
7788462SApril.Chin@Sun.COM state->flags |= FTS_XDEV;
7794887Schin continue;
7804887Schin case 'F':
7814887Schin #if _lib_fsync
7828462SApril.Chin@Sun.COM state->sync = 1;
7834887Schin #else
7844887Schin error(1, "%s not implemented on this system", opt_info.name);
7854887Schin #endif
7864887Schin continue;
7874887Schin case 'H':
7888462SApril.Chin@Sun.COM state->flags |= FTS_META|FTS_PHYSICAL;
7894887Schin path_resolve = 1;
7904887Schin continue;
7914887Schin case 'L':
7928462SApril.Chin@Sun.COM state->flags &= ~FTS_PHYSICAL;
7934887Schin path_resolve = 1;
7944887Schin continue;
7954887Schin case 'P':
7968462SApril.Chin@Sun.COM state->flags &= ~FTS_META;
7978462SApril.Chin@Sun.COM state->flags |= FTS_PHYSICAL;
7984887Schin path_resolve = 1;
7994887Schin continue;
8004887Schin case 'R':
8018462SApril.Chin@Sun.COM state->recursive = 1;
8028462SApril.Chin@Sun.COM state->flags &= ~FTS_META;
8038462SApril.Chin@Sun.COM state->flags |= FTS_PHYSICAL;
8044887Schin path_resolve = 1;
8054887Schin continue;
8064887Schin case 'S':
8078462SApril.Chin@Sun.COM state->suffix = opt_info.arg;
8084887Schin continue;
8094887Schin case 'V':
8104887Schin backup_type = opt_info.arg;
8114887Schin continue;
8124887Schin case '?':
8134887Schin error(ERROR_USAGE|4, "%s", opt_info.arg);
8144887Schin continue;
8154887Schin case ':':
8164887Schin error(2, "%s", opt_info.arg);
8174887Schin continue;
8184887Schin }
8194887Schin break;
8204887Schin }
8214887Schin argc -= opt_info.index + 1;
8224887Schin argv += opt_info.index;
8234887Schin if (*argv && streq(*argv, "-") && !streq(*(argv - 1), "--"))
8244887Schin {
8254887Schin argc--;
8264887Schin argv++;
8274887Schin }
8284887Schin if (!(v = (char**)stkalloc(stkstd, (argc + 2) * sizeof(char*))))
829*12068SRoger.Faulkner@Oracle.COM error(ERROR_SYSTEM|3, "out of space");
8304887Schin memcpy(v, argv, (argc + 1) * sizeof(char*));
8314887Schin argv = v;
8328462SApril.Chin@Sun.COM if (!standard)
8334887Schin {
8348462SApril.Chin@Sun.COM state->wflags |= O_EXCL;
8358462SApril.Chin@Sun.COM if (!argc)
8368462SApril.Chin@Sun.COM {
8378462SApril.Chin@Sun.COM argc++;
8388462SApril.Chin@Sun.COM argv[1] = (char*)dot;
8398462SApril.Chin@Sun.COM }
8404887Schin }
8418462SApril.Chin@Sun.COM if (state->backup)
8424887Schin {
8434887Schin if (!(file = backup_type) && !(backup_type = getenv("VERSION_CONTROL")))
8448462SApril.Chin@Sun.COM state->backup = BAK_existing;
8454887Schin else
8464887Schin switch (strkey(backup_type))
8474887Schin {
8484887Schin case HASHKEY6('e','x','i','s','t','i'):
8494887Schin case HASHKEY5('e','x','i','s','t'):
8504887Schin case HASHKEY4('e','x','i','s'):
8514887Schin case HASHKEY3('e','x','i'):
8524887Schin case HASHKEY2('e','x'):
8534887Schin case HASHKEY1('e'):
8544887Schin case HASHKEY3('n','i','l'):
8554887Schin case HASHKEY2('n','i'):
8568462SApril.Chin@Sun.COM state->backup = BAK_existing;
8574887Schin break;
8584887Schin case HASHKEY5('n','e','v','e','r'):
8594887Schin case HASHKEY4('n','e','v','e'):
8604887Schin case HASHKEY3('n','e','v'):
8614887Schin case HASHKEY2('n','e'):
8624887Schin case HASHKEY6('s','i','m','p','l','e'):
8634887Schin case HASHKEY5('s','i','m','p','l'):
8644887Schin case HASHKEY4('s','i','m','p'):
8654887Schin case HASHKEY3('s','i','m'):
8664887Schin case HASHKEY2('s','i'):
8674887Schin case HASHKEY1('s'):
8688462SApril.Chin@Sun.COM state->backup = BAK_simple;
8694887Schin break;
8704887Schin case HASHKEY6('n','u','m','b','e','r'):
8714887Schin case HASHKEY5('n','u','m','b','e'):
8724887Schin case HASHKEY4('n','u','m','b'):
8734887Schin case HASHKEY3('n','u','m'):
8744887Schin case HASHKEY2('n','u'):
8754887Schin case HASHKEY1('t'):
8768462SApril.Chin@Sun.COM state->backup = BAK_number;
8774887Schin break;
8784887Schin default:
8794887Schin if (file)
8804887Schin error(2, "%s: unknown backup type", backup_type);
8814887Schin break;
8824887Schin }
8838462SApril.Chin@Sun.COM if (!state->suffix && !(state->suffix = getenv("SIMPLE_BACKUP_SUFFIX")))
8848462SApril.Chin@Sun.COM state->suffix = "~";
8858462SApril.Chin@Sun.COM state->suflen = strlen(state->suffix);
8864887Schin }
8874887Schin if (argc <= 0 || error_info.errors)
8884887Schin error(ERROR_USAGE|4, "%s", optusage(NiL));
8894887Schin if (!path_resolve)
8908462SApril.Chin@Sun.COM state->flags |= fts_flags();
8914887Schin file = argv[argc];
8924887Schin argv[argc] = 0;
8934887Schin if (s = strrchr(file, '/'))
8944887Schin {
8954887Schin while (*s == '/')
8964887Schin s++;
8974887Schin if (!(!*s || *s == '.' && (!*++s || *s == '.' && !*++s)))
8984887Schin s = 0;
8994887Schin }
9004887Schin if (file != (char*)dot)
9014887Schin pathcanon(file, 0);
9028462SApril.Chin@Sun.COM if (!(state->directory = !stat(file, &st) && S_ISDIR(st.st_mode)) && argc > 1)
9034887Schin error(ERROR_USAGE|4, "%s", optusage(NiL));
9048462SApril.Chin@Sun.COM if (s && !state->directory)
9054887Schin error(3, "%s: not a directory", file);
9068462SApril.Chin@Sun.COM if ((state->fs3d = fs3d(FS3D_TEST)) && strmatch(file, "...|*/...|.../*"))
9078462SApril.Chin@Sun.COM state->official = 1;
9088462SApril.Chin@Sun.COM state->postsiz = strlen(file);
9098462SApril.Chin@Sun.COM if (state->pathsiz < roundof(state->postsiz + 2, PATH_CHUNK) && !(state->path = newof(state->path, char, state->pathsiz = roundof(state->postsiz + 2, PATH_CHUNK), 0)))
910*12068SRoger.Faulkner@Oracle.COM error(ERROR_SYSTEM|3, "out of space");
9118462SApril.Chin@Sun.COM memcpy(state->path, file, state->postsiz + 1);
9128462SApril.Chin@Sun.COM if (state->directory && state->path[state->postsiz - 1] != '/')
9138462SApril.Chin@Sun.COM state->path[state->postsiz++] = '/';
9148462SApril.Chin@Sun.COM if (state->hierarchy)
9154887Schin {
9168462SApril.Chin@Sun.COM if (!state->directory)
9174887Schin error(3, "%s: last argument must be a directory", file);
9188462SApril.Chin@Sun.COM state->missmode = st.st_mode;
9194887Schin }
9208462SApril.Chin@Sun.COM state->perm = state->uid ? S_IPERM : (S_IPERM & ~S_ISVTX);
9218462SApril.Chin@Sun.COM if (!state->recursive)
9228462SApril.Chin@Sun.COM state->flags |= FTS_TOP;
9238462SApril.Chin@Sun.COM if (fts = fts_open(argv, state->flags, NiL))
9244887Schin {
9258462SApril.Chin@Sun.COM while (!sh_checksig(context) && (ent = fts_read(fts)) && !visit(state, ent));
9264887Schin fts_close(fts);
9274887Schin }
9288462SApril.Chin@Sun.COM else if (state->link != pathsetlink)
9298462SApril.Chin@Sun.COM switch (state->op)
9304887Schin {
9314887Schin case CP:
9324887Schin error(ERROR_SYSTEM|2, "%s: cannot copy", argv[0]);
9334887Schin break;
9344887Schin case LN:
9354887Schin error(ERROR_SYSTEM|2, "%s: cannot link", argv[0]);
9364887Schin break;
9374887Schin case MV:
9384887Schin error(ERROR_SYSTEM|2, "%s: cannot move", argv[0]);
9394887Schin break;
9404887Schin }
9418462SApril.Chin@Sun.COM else if ((*state->link)(*argv, state->path))
9428462SApril.Chin@Sun.COM error(ERROR_SYSTEM|2, "%s: cannot link to %s", *argv, state->path);
943*12068SRoger.Faulkner@Oracle.COM if (cleanup && !sh)
944*12068SRoger.Faulkner@Oracle.COM {
945*12068SRoger.Faulkner@Oracle.COM if (state->path)
946*12068SRoger.Faulkner@Oracle.COM free(state->path);
947*12068SRoger.Faulkner@Oracle.COM free(state);
948*12068SRoger.Faulkner@Oracle.COM }
9494887Schin return error_info.errors != 0;
9504887Schin }
951