Posted By

wkblackman on 11/18/09


Tagged

c


Versions (?)

C


 / Published in: C
 

sdf

  1. /*-
  2.  * Copyright (c) 1988, 1993, 1994
  3.  * The Regents of the University of California. All rights reserved.
  4.  *
  5.  * This code is derived from software contributed to Berkeley by
  6.  * David Hitz of Auspex Systems Inc.
  7.  *
  8.  * Redistribution and use in source and binary forms, with or without
  9.  * modification, are permitted provided that the following conditions
  10.  * are met:
  11.  * 1. Redistributions of source code must retain the above copyright
  12.  * notice, this list of conditions and the following disclaimer.
  13.  * 2. Redistributions in binary form must reproduce the above copyright
  14.  * notice, this list of conditions and the following disclaimer in the
  15.  * documentation and/or other materials provided with the distribution.
  16.  * 4. Neither the name of the University nor the names of its contributors
  17.  * may be used to endorse or promote products derived from this software
  18.  * without specific prior written permission.
  19.  *
  20.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  21.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  22.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  23.  * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  24.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  25.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  26.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  27.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  28.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  29.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  30.  * SUCH DAMAGE.
  31.  */
  32.  
  33. #if 0
  34. #ifndef lint
  35. static char const copyright[] =
  36. "@(#) Copyright (c) 1988, 1993, 1994\n\
  37. The Regents of the University of California. All rights reserved.\n";
  38. #endif /* not lint */
  39.  
  40. #ifndef lint
  41. static char sccsid[] = "@(#)cp.c 8.2 (Berkeley) 4/1/94";
  42. #endif /* not lint */
  43. #endif
  44. #include <sys/cdefs.h>
  45. __FBSDID("$FreeBSD: src/bin/cp/cp.c,v 1.57.2.1.4.1 2009/04/15 03:14:26 kensmith Exp $");
  46.  
  47. /*
  48.  * Cp copies source files to target files.
  49.  *
  50.  * The global PATH_T structure "to" always contains the path to the
  51.  * current target file. Since fts(3) does not change directories,
  52.  * this path can be either absolute or dot-relative.
  53.  *
  54.  * The basic algorithm is to initialize "to" and use fts(3) to traverse
  55.  * the file hierarchy rooted in the argument list. A trivial case is the
  56.  * case of 'cp file1 file2'. The more interesting case is the case of
  57.  * 'cp file1 file2 ... fileN dir' where the hierarchy is traversed and the
  58.  * path (relative to the root of the traversal) is appended to dir (stored
  59.  * in "to") to form the final target path.
  60.  */
  61.  
  62. #include <sys/types.h>
  63. #include <sys/stat.h>
  64.  
  65. #include <err.h>
  66. #include <errno.h>
  67. #include <fts.h>
  68. #include <limits.h>
  69. #include <signal.h>
  70. #include <stdio.h>
  71. #include <stdlib.h>
  72. #include <string.h>
  73. #include <unistd.h>
  74.  
  75. #include "extern.h"
  76.  
  77. #define STRIP_TRAILING_SLASH(p) { \
  78. while ((p).p_end > (p).p_path + 1 && (p).p_end[-1] == '/') \
  79. *--(p).p_end = 0; \
  80. }
  81.  
  82. static char emptystring[] = "";
  83.  
  84. PATH_T to = { to.p_path, emptystring, "" };
  85.  
  86. int fflag, iflag, lflag, nflag, pflag, vflag;
  87. static int Rflag, rflag;
  88. volatile sig_atomic_t info;
  89.  
  90. enum op { FILE_TO_FILE, FILE_TO_DIR, DIR_TO_DNE };
  91.  
  92. static int copy(char *[], enum op, int);
  93. static int mastercmp(const FTSENT * const *, const FTSENT * const *);
  94. static void siginfo(int __unused);
  95.  
  96. int
  97. main(int argc, char *argv[])
  98. {
  99. struct stat to_stat, tmp_stat;
  100. enum op type;
  101. int Hflag, Lflag, Pflag, ch, fts_options, r, have_trailing_slash;
  102. char *target;
  103.  
  104. Hflag = Lflag = Pflag = 0;
  105. while ((ch = getopt(argc, argv, "HLPRafilnprv")) != -1)
  106. switch (ch) {
  107. case 'H':
  108. Hflag = 1;
  109. Lflag = Pflag = 0;
  110. break;
  111. case 'L':
  112. Lflag = 1;
  113. Hflag = Pflag = 0;
  114. break;
  115. case 'P':
  116. Pflag = 1;
  117. Hflag = Lflag = 0;
  118. break;
  119. case 'R':
  120. Rflag = 1;
  121. break;
  122. case 'a':
  123. Pflag = 1;
  124. pflag = 1;
  125. Rflag = 1;
  126. Hflag = Lflag = 0;
  127. break;
  128. case 'f':
  129. fflag = 1;
  130. iflag = nflag = 0;
  131. break;
  132. case 'i':
  133. iflag = 1;
  134. fflag = nflag = 0;
  135. break;
  136. case 'l':
  137. lflag = 1;
  138. break;
  139. case 'n':
  140. nflag = 1;
  141. fflag = iflag = 0;
  142. break;
  143. case 'p':
  144. pflag = 1;
  145. break;
  146. case 'r':
  147. rflag = Lflag = 1;
  148. Hflag = Pflag = 0;
  149. break;
  150. case 'v':
  151. vflag = 1;
  152. break;
  153. default:
  154. usage();
  155. break;
  156. }
  157. argc -= optind;
  158. argv += optind;
  159.  
  160. if (argc < 2)
  161. usage();
  162.  
  163. fts_options = FTS_NOCHDIR | FTS_PHYSICAL;
  164. if (Rflag && rflag)
  165. errx(1, "the -R and -r options may not be specified together");
  166. if (rflag)
  167. Rflag = 1;
  168. if (Rflag) {
  169. if (Hflag)
  170. fts_options |= FTS_COMFOLLOW;
  171. if (Lflag) {
  172. fts_options &= ~FTS_PHYSICAL;
  173. fts_options |= FTS_LOGICAL;
  174. }
  175. } else {
  176. fts_options &= ~FTS_PHYSICAL;
  177. fts_options |= FTS_LOGICAL | FTS_COMFOLLOW;
  178. }
  179. (void)signal(SIGINFO, siginfo);
  180.  
  181. /* Save the target base in "to". */
  182. target = argv[--argc];
  183. if (strlcpy(to.p_path, target, sizeof(to.p_path)) >= sizeof(to.p_path))
  184. errx(1, "%s: name too long", target);
  185. to.p_end = to.p_path + strlen(to.p_path);
  186. if (to.p_path == to.p_end) {
  187. *to.p_end++ = '.';
  188. *to.p_end = 0;
  189. }
  190. have_trailing_slash = (to.p_end[-1] == '/');
  191. if (have_trailing_slash)
  192. STRIP_TRAILING_SLASH(to);
  193. to.target_end = to.p_end;
  194.  
  195. /* Set end of argument list for fts(3). */
  196. argv[argc] = NULL;
  197.  
  198. /*
  199. * Cp has two distinct cases:
  200. *
  201. * cp [-R] source target
  202. * cp [-R] source1 ... sourceN directory
  203. *
  204. * In both cases, source can be either a file or a directory.
  205. *
  206. * In (1), the target becomes a copy of the source. That is, if the
  207. * source is a file, the target will be a file, and likewise for
  208. * directories.
  209. *
  210. * In (2), the real target is not directory, but "directory/source".
  211. */
  212. r = stat(to.p_path, &to_stat);
  213. if (r == -1 && errno != ENOENT)
  214. err(1, "%s", to.p_path);
  215. if (r == -1 || !S_ISDIR(to_stat.st_mode)) {
  216. /*
  217. * Case (1). Target is not a directory.
  218. */
  219. if (argc > 1) {
  220. usage();
  221. exit(1);
  222. }
  223. /*
  224. * Need to detect the case:
  225. * cp -R dir foo
  226. * Where dir is a directory and foo does not exist, where
  227. * we want pathname concatenations turned on but not for
  228. * the initial mkdir().
  229. */
  230. if (r == -1) {
  231. if (Rflag && (Lflag || Hflag))
  232. stat(*argv, &tmp_stat);
  233. else
  234. lstat(*argv, &tmp_stat);
  235.  
  236. if (S_ISDIR(tmp_stat.st_mode) && Rflag)
  237. type = DIR_TO_DNE;
  238. else
  239. type = FILE_TO_FILE;
  240. } else
  241. type = FILE_TO_FILE;
  242.  
  243. if (have_trailing_slash && type == FILE_TO_FILE) {
  244. if (r == -1)
  245. errx(1, "directory %s does not exist",
  246. to.p_path);
  247. else
  248. errx(1, "%s is not a directory", to.p_path);
  249. }
  250. } else
  251. /*
  252. * Case (2). Target is a directory.
  253. */
  254. type = FILE_TO_DIR;
  255.  
  256. exit (copy(argv, type, fts_options));
  257. }
  258.  
  259. static int
  260. copy(char *argv[], enum op type, int fts_options)
  261. {
  262. struct stat to_stat;
  263. FTS *ftsp;
  264. FTSENT *curr;
  265. int base = 0, dne, badcp, rval;
  266. size_t nlen;
  267. char *p, *target_mid;
  268. mode_t mask, mode;
  269.  
  270. /*
  271. * Keep an inverted copy of the umask, for use in correcting
  272. * permissions on created directories when not using -p.
  273. */
  274. mask = ~umask(0777);
  275. umask(~mask);
  276.  
  277. if ((ftsp = fts_open(argv, fts_options, mastercmp)) == NULL)
  278. err(1, "fts_open");
  279. for (badcp = rval = 0; (curr = fts_read(ftsp)) != NULL; badcp = 0) {
  280. switch (curr->fts_info) {
  281. case FTS_NS:
  282. case FTS_DNR:
  283. case FTS_ERR:
  284. warnx("%s: %s",
  285. curr->fts_path, strerror(curr->fts_errno));
  286. badcp = rval = 1;
  287. continue;
  288. case FTS_DC: /* Warn, continue. */
  289. warnx("%s: directory causes a cycle", curr->fts_path);
  290. badcp = rval = 1;
  291. continue;
  292. default:
  293. ;
  294. }
  295.  
  296. /*
  297. * If we are in case (2) or (3) above, we need to append the
  298.   * source name to the target name.
  299.   */
  300. if (type != FILE_TO_FILE) {
  301. /*
  302. * Need to remember the roots of traversals to create
  303. * correct pathnames. If there's a directory being
  304. * copied to a non-existent directory, e.g.
  305. * cp -R a/dir noexist
  306. * the resulting path name should be noexist/foo, not
  307. * noexist/dir/foo (where foo is a file in dir), which
  308. * is the case where the target exists.
  309. *
  310. * Also, check for "..". This is for correct path
  311. * concatenation for paths ending in "..", e.g.
  312. * cp -R .. /tmp
  313. * Paths ending in ".." are changed to ".". This is
  314. * tricky, but seems the easiest way to fix the problem.
  315. *
  316. * XXX
  317. * Since the first level MUST be FTS_ROOTLEVEL, base
  318. * is always initialized.
  319. */
  320. if (curr->fts_level == FTS_ROOTLEVEL) {
  321. if (type != DIR_TO_DNE) {
  322. p = strrchr(curr->fts_path, '/');
  323. base = (p == NULL) ? 0 :
  324. (int)(p - curr->fts_path + 1);
  325.  
  326. if (!strcmp(&curr->fts_path[base],
  327. ".."))
  328. base += 1;
  329. } else
  330. base = curr->fts_pathlen;
  331. }
  332.  
  333. p = &curr->fts_path[base];
  334. nlen = curr->fts_pathlen - base;
  335. target_mid = to.target_end;
  336. if (*p != '/' && target_mid[-1] != '/')
  337. *target_mid++ = '/';
  338. *target_mid = 0;
  339. if (target_mid - to.p_path + nlen >= PATH_MAX) {
  340. warnx("%s%s: name too long (not copied)",
  341. to.p_path, p);
  342. badcp = rval = 1;
  343. continue;
  344. }
  345. (void)strncat(target_mid, p, nlen);
  346. to.p_end = target_mid + nlen;
  347. *to.p_end = 0;
  348. STRIP_TRAILING_SLASH(to);
  349. }
  350.  
  351. if (curr->fts_info == FTS_DP) {
  352. /*
  353. * We are nearly finished with this directory. If we
  354. * didn't actually copy it, or otherwise don't need to
  355. * change its attributes, then we are done.
  356. */
  357. if (!curr->fts_number)
  358. continue;
  359. /*
  360. * If -p is in effect, set all the attributes.
  361. * Otherwise, set the correct permissions, limited
  362. * by the umask. Optimise by avoiding a chmod()
  363. * if possible (which is usually the case if we
  364. * made the directory). Note that mkdir() does not
  365. * honour setuid, setgid and sticky bits, but we
  366. * normally want to preserve them on directories.
  367. */
  368. if (pflag) {
  369. if (setfile(curr->fts_statp, -1))
  370. rval = 1;
  371. if (preserve_dir_acls(curr->fts_statp,
  372. curr->fts_accpath, to.p_path) != 0)
  373. rval = 1;
  374. } else {
  375. mode = curr->fts_statp->st_mode;
  376. if ((mode & (S_ISUID | S_ISGID | S_ISTXT)) ||
  377. ((mode | S_IRWXU) & mask) != (mode & mask))
  378. if (chmod(to.p_path, mode & mask) != 0){
  379. warn("chmod: %s", to.p_path);
  380. rval = 1;
  381. }
  382. }
  383. continue;
  384. }
  385.  
  386. /* Not an error but need to remember it happened */
  387. if (stat(to.p_path, &to_stat) == -1)
  388. dne = 1;
  389. else {
  390. if (to_stat.st_dev == curr->fts_statp->st_dev &&
  391. to_stat.st_ino == curr->fts_statp->st_ino) {
  392. warnx("%s and %s are identical (not copied).",
  393. to.p_path, curr->fts_path);
  394. badcp = rval = 1;
  395. if (S_ISDIR(curr->fts_statp->st_mode))
  396. (void)fts_set(ftsp, curr, FTS_SKIP);
  397. continue;
  398. }
  399. if (!S_ISDIR(curr->fts_statp->st_mode) &&
  400. S_ISDIR(to_stat.st_mode)) {
  401. warnx("cannot overwrite directory %s with "
  402. "non-directory %s",
  403. to.p_path, curr->fts_path);
  404. badcp = rval = 1;
  405. continue;
  406. }
  407. dne = 0;
  408. }
  409.  
  410. switch (curr->fts_statp->st_mode & S_IFMT) {
  411. case S_IFLNK:
  412. /* Catch special case of a non-dangling symlink */
  413. if ((fts_options & FTS_LOGICAL) ||
  414. ((fts_options & FTS_COMFOLLOW) &&
  415. curr->fts_level == 0)) {
  416. if (copy_file(curr, dne))
  417. badcp = rval = 1;
  418. } else {
  419. if (copy_link(curr, !dne))
  420. badcp = rval = 1;
  421. }
  422. break;
  423. case S_IFDIR:
  424. if (!Rflag) {
  425. warnx("%s is a directory (not copied).",
  426. curr->fts_path);
  427. (void)fts_set(ftsp, curr, FTS_SKIP);
  428. badcp = rval = 1;
  429. break;
  430. }
  431. /*
  432. * If the directory doesn't exist, create the new
  433. * one with the from file mode plus owner RWX bits,
  434. * modified by the umask. Trade-off between being
  435. * able to write the directory (if from directory is
  436. * 555) and not causing a permissions race. If the
  437. * umask blocks owner writes, we fail..
  438. */
  439. if (dne) {
  440. if (mkdir(to.p_path,
  441. curr->fts_statp->st_mode | S_IRWXU) < 0)
  442. err(1, "%s", to.p_path);
  443. } else if (!S_ISDIR(to_stat.st_mode)) {
  444. errno = ENOTDIR;
  445. err(1, "%s", to.p_path);
  446. }
  447. /*
  448. * Arrange to correct directory attributes later
  449. * (in the post-order phase) if this is a new
  450. * directory, or if the -p flag is in effect.
  451. */
  452. curr->fts_number = pflag || dne;
  453. break;
  454. case S_IFBLK:
  455. case S_IFCHR:
  456. if (Rflag) {
  457. if (copy_special(curr->fts_statp, !dne))
  458. badcp = rval = 1;
  459. } else {
  460. if (copy_file(curr, dne))
  461. badcp = rval = 1;
  462. }
  463. break;
  464. case S_IFSOCK:
  465. warnx("%s is a socket (not copied).",
  466. curr->fts_path);
  467. case S_IFIFO:
  468. if (Rflag) {
  469. if (copy_fifo(curr->fts_statp, !dne))
  470. badcp = rval = 1;
  471. } else {
  472. if (copy_file(curr, dne))
  473. badcp = rval = 1;
  474. }
  475. break;
  476. default:
  477. if (copy_file(curr, dne))
  478. badcp = rval = 1;
  479. break;
  480. }
  481. if (vflag && !badcp)
  482. (void)printf("%s -> %s\n", curr->fts_path, to.p_path);
  483. }
  484. if (errno)
  485. err(1, "fts_read");
  486. fts_close(ftsp);
  487. return (rval);
  488. }
  489.  
  490. /*
  491.  * mastercmp --
  492.  * The comparison function for the copy order. The order is to copy
  493.  * non-directory files before directory files. The reason for this
  494.  * is because files tend to be in the same cylinder group as their
  495.  * parent directory, whereas directories tend not to be. Copying the
  496.  * files first reduces seeking.
  497.  */
  498. static int
  499. mastercmp(const FTSENT * const *a, const FTSENT * const *b)
  500. {
  501. int a_info, b_info;
  502.  
  503. a_info = (*a)->fts_info;
  504. if (a_info == FTS_ERR || a_info == FTS_NS || a_info == FTS_DNR)
  505. return (0);
  506. b_info = (*b)->fts_info;
  507. if (b_info == FTS_ERR || b_info == FTS_NS || b_info == FTS_DNR)
  508. return (0);
  509. if (a_info == FTS_D)
  510. return (-1);
  511. if (b_info == FTS_D)
  512. return (1);
  513. return (0);
  514. }
  515.  
  516. static void
  517. siginfo(int sig __unused)
  518. {
  519.  
  520. info = 1;
  521. }

Report this snippet  

You need to login to post a comment.