Print this page
patch tsoome-feedback
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/cmd/mv/mv.c
+++ new/usr/src/cmd/mv/mv.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
13 13 * When distributing Covered Code, include this CDDL HEADER in each
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 21
22 22 /*
23 23 * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
24 24 */
25 25
26 26 /*
27 27 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
28 28 * Use is subject to license terms.
29 29 */
30 30
31 31 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
32 32 /* All Rights Reserved */
33 33
34 34 /*
35 35 * University Copyright- Copyright (c) 1982, 1986, 1988
36 36 * The Regents of the University of California
37 37 * All Rights Reserved
38 38 *
39 39 * University Acknowledgment- Portions of this document are derived from
40 40 * software developed by the University of California, Berkeley, and its
41 41 * contributors.
42 42 */
43 43
44 44 /*
45 45 * Combined mv/cp/ln command:
46 46 * mv file1 file2
47 47 * mv dir1 dir2
48 48 * mv file1 ... filen dir1
49 49 */
50 50 #include <sys/time.h>
51 51 #include <signal.h>
52 52 #include <locale.h>
53 53 #include <stdarg.h>
54 54 #include <sys/acl.h>
55 55 #include <libcmdutils.h>
56 56 #include <aclutils.h>
57 57 #include "getresponse.h"
58 58
59 59 #define FTYPE(A) (A.st_mode)
60 60 #define FMODE(A) (A.st_mode)
61 61 #define UID(A) (A.st_uid)
62 62 #define GID(A) (A.st_gid)
63 63 #define IDENTICAL(A, B) (A.st_dev == B.st_dev && A.st_ino == B.st_ino)
64 64 #define ISDIR(A) ((A.st_mode & S_IFMT) == S_IFDIR)
65 65 #define ISDOOR(A) ((A.st_mode & S_IFMT) == S_IFDOOR)
66 66 #define ISLNK(A) ((A.st_mode & S_IFMT) == S_IFLNK)
67 67 #define ISREG(A) (((A).st_mode & S_IFMT) == S_IFREG)
68 68 #define ISDEV(A) ((A.st_mode & S_IFMT) == S_IFCHR || \
69 69 (A.st_mode & S_IFMT) == S_IFBLK || \
70 70 (A.st_mode & S_IFMT) == S_IFIFO)
71 71 #define ISSOCK(A) ((A.st_mode & S_IFMT) == S_IFSOCK)
72 72
73 73 #define DELIM '/'
74 74 #define EQ(x, y) (strcmp(x, y) == 0)
75 75 #define FALSE 0
76 76 #define MODEBITS (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)
77 77 #define TRUE 1
78 78
79 79 static char *dname(char *);
80 80 static int lnkfil(char *, char *);
81 81 static int cpymve(char *, char *);
82 82 static int chkfiles(char *, char **);
83 83 static int rcopy(char *, char *);
84 84 static int chk_different(char *, char *);
85 85 static int chg_time(char *, struct stat);
86 86 static int chg_mode(char *, uid_t, gid_t, mode_t);
87 87 static int copydir(char *, char *);
88 88 static int copyspecial(char *);
89 89 static int getrealpath(char *, char *);
90 90 static void usage(void);
91 91 static void Perror(char *);
92 92 static void Perror2(char *, char *);
93 93 static int use_stdin(void);
94 94 static int copyattributes(char *, char *);
95 95 static int copy_sysattr(char *, char *);
96 96 static tree_node_t *create_tnode(dev_t, ino_t);
97 97
98 98 static struct stat s1, s2, s3, s4;
99 99 static int cpy = FALSE;
100 100 static int mve = FALSE;
101 101 static int lnk = FALSE;
102 102 static char *cmd;
103 103 static int silent = 0;
104 104 static int fflg = 0;
105 105 static int iflg = 0;
106 106 static int pflg = 0;
107 107 static int Rflg = 0; /* recursive copy */
108 108 static int rflg = 0; /* recursive copy */
109 109 static int sflg = 0;
110 110 static int Hflg = 0; /* follow cmd line arg symlink to dir */
111 111 static int Lflg = 0; /* follow symlinks */
112 112 static int Pflg = 0; /* do not follow symlinks */
113 113 static int atflg = 0;
114 114 static int attrsilent = 0;
115 115 static int targetexists = 0;
116 116 static int cmdarg; /* command line argument */
117 117 static avl_tree_t *stree = NULL; /* source file inode search tree */
118 118 static acl_t *s1acl;
119 119 static int saflg = 0; /* 'cp' extended system attr. */
120 120 static int srcfd = -1;
121 121 static int targfd = -1;
122 122 static int sourcedirfd = -1;
123 123 static int targetdirfd = -1;
124 124 static DIR *srcdirp = NULL;
125 125 static int srcattrfd = -1;
126 126 static int targattrfd = -1;
127 127 static struct stat attrdir;
128 128
129 129 /* Extended system attributes support */
130 130
131 131 static int open_source(char *);
132 132 static int open_target_srctarg_attrdirs(char *, char *);
133 133 static int open_attrdirp(char *);
134 134 static int traverse_attrfile(struct dirent *, char *, char *, int);
135 135 static void rewind_attrdir(DIR *);
136 136 static void close_all();
137 137
138 138
139 139 int
140 140 main(int argc, char *argv[])
141 141 {
142 142 int c, i, r, errflg = 0;
143 143 char target[PATH_MAX];
144 144 int (*move)(char *, char *);
145 145
146 146 /*
147 147 * Determine command invoked (mv, cp, or ln)
148 148 */
149 149
150 150 if (cmd = strrchr(argv[0], '/'))
151 151 ++cmd;
152 152 else
153 153 cmd = argv[0];
154 154
155 155 /*
156 156 * Set flags based on command.
157 157 */
158 158
159 159 (void) setlocale(LC_ALL, "");
160 160 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
161 161 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
162 162 #endif
163 163 (void) textdomain(TEXT_DOMAIN);
164 164 if (init_yes() < 0) {
165 165 (void) fprintf(stderr, gettext(ERR_MSG_INIT_YES),
166 166 strerror(errno));
167 167 exit(3);
168 168 }
169 169
170 170 if (EQ(cmd, "mv"))
171 171 mve = TRUE;
172 172 else if (EQ(cmd, "ln"))
173 173 lnk = TRUE;
174 174 else if (EQ(cmd, "cp"))
175 175 cpy = TRUE;
176 176 else {
177 177 (void) fprintf(stderr,
178 178 gettext("Invalid command name (%s); expecting "
179 179 "mv, cp, or ln.\n"), cmd);
180 180 exit(1);
181 181 }
182 182
183 183 /*
184 184 * Check for options:
185 185 * cp [ -r|-R [-H|-L|-P]] [-afip@/] file1 [file2 ...] target
186 186 * cp [-afiprR@/] file1 [file2 ...] target
187 187 * ln [-f] [-n] [-s] file1 [file2 ...] target
188 188 * ln [-f] [-n] [-s] file1 [file2 ...]
189 189 * mv [-f|i] file1 [file2 ...] target
190 190 * mv [-f|i] dir1 target
191 191 */
192 192
193 193 if (cpy) {
194 194 while ((c = getopt(argc, argv, "afHiLpPrR@/")) != EOF)
195 195 switch (c) {
196 196 case 'f':
197 197 fflg++;
198 198 break;
199 199 case 'i':
200 200 iflg++;
201 201 break;
202 202 case 'p':
203 203 pflg++;
204 204 #ifdef XPG4
205 205 attrsilent = 1;
206 206 atflg = 0;
207 207 saflg = 0;
208 208 #else
209 209 if (atflg == 0)
210 210 attrsilent = 1;
211 211 #endif
212 212 break;
213 213 case 'H':
214 214 /*
215 215 * If more than one of -H, -L, or -P are
216 216 * specified, only the last option specified
217 217 * determines the behavior.
218 218 */
219 219 Lflg = Pflg = 0;
220 220 Hflg++;
221 221 break;
222 222 case 'L':
223 223 Hflg = Pflg = 0;
224 224 Lflg++;
225 225 break;
226 226 case 'P':
227 227 Lflg = Hflg = 0;
228 228 Pflg++;
229 229 break;
230 230 case 'R':
231 231 /*
232 232 * The default behavior of cp -R|-r
233 233 * when specified without -H|-L|-P
234 234 * is -L.
235 235 */
236 236 Rflg++;
237 237 /*FALLTHROUGH*/
238 238 case 'r':
239 239 rflg++;
240 240 break;
241 241 case 'a':
242 242 Lflg = Hflg = 0;
243 243 pflg++;
244 244 Pflg++;
245 245 Rflg++;
246 246 rflg++;
247 247 break;
248 248 case '@':
249 249 atflg++;
250 250 attrsilent = 0;
251 251 #ifdef XPG4
252 252 pflg = 0;
253 253 #endif
254 254 break;
255 255 case '/':
256 256 saflg++;
257 257 attrsilent = 0;
258 258 #ifdef XPG4
259 259 pflg = 0;
260 260 #endif
261 261 break;
262 262 default:
263 263 errflg++;
264 264 }
265 265
266 266 /* -R or -r must be specified with -H, -L, or -P */
267 267 if ((Hflg || Lflg || Pflg) && !(Rflg || rflg)) {
268 268 errflg++;
269 269 }
270 270
271 271 } else if (mve) {
272 272 while ((c = getopt(argc, argv, "fis")) != EOF)
273 273 switch (c) {
274 274 case 'f':
275 275 silent++;
276 276 #ifdef XPG4
277 277 iflg = 0;
278 278 #endif
279 279 break;
280 280 case 'i':
281 281 iflg++;
282 282 #ifdef XPG4
283 283 silent = 0;
284 284 #endif
285 285 break;
286 286 default:
287 287 errflg++;
288 288 }
289 289 } else { /* ln */
290 290 while ((c = getopt(argc, argv, "fns")) != EOF)
291 291 switch (c) {
292 292 case 'f':
293 293 silent++;
294 294 break;
295 295 case 'n':
296 296 /* silently ignored; this is the default */
297 297 break;
298 298 case 's':
299 299 sflg++;
300 300 break;
301 301 default:
302 302 errflg++;
303 303 }
304 304 }
305 305
306 306 /*
307 307 * For BSD compatibility allow - to delimit the end of
308 308 * options for mv.
309 309 */
310 310 if (mve && optind < argc && (strcmp(argv[optind], "-") == 0))
311 311 optind++;
312 312
313 313 /*
314 314 * Check for sufficient arguments
315 315 * or a usage error.
316 316 */
317 317
318 318 argc -= optind;
319 319 argv = &argv[optind];
320 320
321 321 if ((argc < 2 && lnk != TRUE) || (argc < 1 && lnk == TRUE)) {
322 322 (void) fprintf(stderr,
323 323 gettext("%s: Insufficient arguments (%d)\n"),
324 324 cmd, argc);
325 325 usage();
326 326 }
327 327
328 328 if (errflg != 0)
329 329 usage();
330 330
331 331 /*
332 332 * If there is more than a source and target,
333 333 * the last argument (the target) must be a directory
334 334 * which really exists.
335 335 */
336 336
337 337 if (argc > 2) {
338 338 if (stat(argv[argc-1], &s2) < 0) {
339 339 (void) fprintf(stderr,
340 340 gettext("%s: %s not found\n"),
341 341 cmd, argv[argc-1]);
342 342 exit(2);
343 343 }
344 344
345 345 if (!ISDIR(s2)) {
346 346 (void) fprintf(stderr,
347 347 gettext("%s: Target %s must be a directory\n"),
348 348 cmd, argv[argc-1]);
349 349 usage();
350 350 }
351 351 }
352 352
353 353 if (strlen(argv[argc-1]) >= PATH_MAX) {
354 354 (void) fprintf(stderr,
355 355 gettext("%s: Target %s file name length exceeds PATH_MAX"
356 356 " %d\n"), cmd, argv[argc-1], PATH_MAX);
357 357 exit(78);
358 358 }
359 359
360 360 if (argc == 1) {
361 361 if (!lnk)
362 362 usage();
363 363 (void) strcpy(target, ".");
364 364 } else {
365 365 (void) strcpy(target, argv[--argc]);
366 366 }
367 367
368 368 /*
369 369 * Perform a multiple argument mv|cp|ln by
370 370 * multiple invocations of cpymve() or lnkfil().
371 371 */
372 372 if (lnk)
373 373 move = lnkfil;
374 374 else
375 375 move = cpymve;
376 376
377 377 r = 0;
378 378 for (i = 0; i < argc; i++) {
379 379 stree = NULL;
380 380 cmdarg = 1;
381 381 r += move(argv[i], target);
382 382 }
383 383
384 384 /*
385 385 * Show errors by nonzero exit code.
386 386 */
387 387
388 388 return (r?2:0);
389 389 }
390 390
391 391 static int
392 392 lnkfil(char *source, char *target)
393 393 {
394 394 char *buf = NULL;
395 395
396 396 if (sflg) {
397 397
398 398 /*
399 399 * If target is a directory make complete
400 400 * name of the new symbolic link within that
401 401 * directory.
402 402 */
403 403
404 404 if ((stat(target, &s2) >= 0) && ISDIR(s2)) {
405 405 size_t len;
406 406
407 407 len = strlen(target) + strlen(dname(source)) + 4;
408 408 if ((buf = (char *)malloc(len)) == NULL) {
409 409 (void) fprintf(stderr,
410 410 gettext("%s: Insufficient memory "
411 411 "to %s %s\n"), cmd, cmd, source);
412 412 exit(3);
413 413 }
414 414 (void) snprintf(buf, len, "%s/%s",
415 415 target, dname(source));
416 416 target = buf;
417 417 }
418 418
419 419 /*
420 420 * Check to see if the file exists already.
421 421 * In this case we use lstat() instead of stat():
422 422 * unlink(2) and symlink(2) will operate on the file
423 423 * itself, not its reference, if the file is a symlink.
424 424 */
425 425
426 426 if ((lstat(target, &s2) == 0)) {
427 427 /*
428 428 * Check if the silent flag is set ie. the -f option
429 429 * is used. If so, use unlink to remove the current
430 430 * target to replace with the new target, specified
431 431 * on the command line. Proceed with symlink.
432 432 */
433 433 if (silent) {
434 434 /*
435 435 * Don't allow silent (-f) removal of an existing
436 436 * directory; could leave unreferenced directory
437 437 * entries.
438 438 */
439 439 if (ISDIR(s2)) {
440 440 (void) fprintf(stderr,
441 441 gettext("%s: cannot create link "
442 442 "over directory %s\n"), cmd,
443 443 target);
444 444 return (1);
445 445 }
446 446 if (unlink(target) < 0) {
447 447 (void) fprintf(stderr,
448 448 gettext("%s: cannot unlink %s: "),
449 449 cmd, target);
450 450 perror("");
451 451 return (1);
452 452 }
453 453 }
454 454 }
455 455
456 456
457 457 /*
458 458 * Create a symbolic link to the source.
459 459 */
460 460
461 461 if (symlink(source, target) < 0) {
462 462 (void) fprintf(stderr,
463 463 gettext("%s: cannot create %s: "),
464 464 cmd, target);
465 465 perror("");
466 466 if (buf != NULL)
467 467 free(buf);
468 468 return (1);
469 469 }
470 470 if (buf != NULL)
471 471 free(buf);
472 472 return (0);
473 473 }
474 474
475 475 switch (chkfiles(source, &target)) {
476 476 case 1: return (1);
477 477 case 2: return (0);
478 478 /* default - fall through */
479 479 }
480 480
481 481 /*
482 482 * Make sure source file is not a directory,
483 483 * we cannot link directories...
484 484 */
485 485
486 486 if (ISDIR(s1)) {
487 487 (void) fprintf(stderr,
488 488 gettext("%s: %s is a directory\n"), cmd, source);
489 489 return (1);
490 490 }
491 491
492 492 /*
493 493 * hard link, call link() and return.
494 494 */
495 495
496 496 if (link(source, target) < 0) {
497 497 if (errno == EXDEV)
498 498 (void) fprintf(stderr,
499 499 gettext("%s: %s is on a different file system\n"),
500 500 cmd, target);
501 501 else {
502 502 (void) fprintf(stderr,
503 503 gettext("%s: cannot create link %s: "),
504 504 cmd, target);
505 505 perror("");
506 506 }
507 507 if (buf != NULL)
508 508 free(buf);
509 509 return (1);
510 510 } else {
511 511 if (buf != NULL)
512 512 free(buf);
513 513 return (0);
514 514 }
515 515 }
516 516
517 517 static int
518 518 cpymve(char *source, char *target)
519 519 {
520 520 int n;
521 521 int fi, fo;
522 522 int ret = 0;
523 523 int attret = 0;
524 524 int sattret = 0;
525 525 int errno_save;
526 526 int error = 0;
527 527
528 528 switch (chkfiles(source, &target)) {
529 529 case 1: return (1);
530 530 case 2: return (0);
531 531 /* default - fall through */
532 532 }
533 533
534 534 /*
535 535 * If it's a recursive copy and source
536 536 * is a directory, then call rcopy (from copydir).
537 537 */
538 538 if (cpy) {
539 539 if (ISDIR(s1)) {
540 540 int rc;
541 541 avl_index_t where = 0;
542 542 tree_node_t *tnode;
543 543 tree_node_t *tptr;
544 544 dev_t save_dev = s1.st_dev;
545 545 ino_t save_ino = s1.st_ino;
546 546
547 547 /*
548 548 * We will be recursing into the directory so
549 549 * save the inode information to a search tree
550 550 * to avoid getting into an endless loop.
551 551 */
552 552 if ((rc = add_tnode(&stree, save_dev, save_ino)) != 1) {
553 553 if (rc == 0) {
554 554 /*
555 555 * We've already visited this directory.
556 556 * Don't remove the search tree entry
557 557 * to make sure we don't get into an
558 558 * endless loop if revisited from a
559 559 * different part of the hierarchy.
560 560 */
561 561 (void) fprintf(stderr, gettext(
562 562 "%s: cycle detected: %s\n"),
563 563 cmd, source);
564 564 } else {
565 565 Perror(source);
566 566 }
567 567 return (1);
568 568 }
569 569
570 570 cmdarg = 0;
571 571 rc = copydir(source, target);
572 572
573 573 /*
574 574 * Create a tnode to get an index to the matching
575 575 * node (same dev and inode) in the search tree,
576 576 * then use the index to remove the matching node
577 577 * so it we do not wrongly detect a cycle when
578 578 * revisiting this directory from another part of
579 579 * the hierarchy.
580 580 */
581 581 if ((tnode = create_tnode(save_dev,
582 582 save_ino)) == NULL) {
583 583 Perror(source);
584 584 return (1);
585 585 }
586 586 if ((tptr = avl_find(stree, tnode, &where)) != NULL) {
587 587 avl_remove(stree, tptr);
588 588 }
589 589 free(tptr);
590 590 free(tnode);
591 591 return (rc);
592 592
593 593 } else if (ISDEV(s1) && Rflg) {
594 594 return (copyspecial(target));
595 595 } else {
596 596 goto copy;
597 597 }
598 598 }
599 599
600 600 if (mve) {
601 601 if (rename(source, target) >= 0)
602 602 return (0);
603 603 if (errno != EXDEV) {
604 604 if (errno == ENOTDIR && ISDIR(s1)) {
605 605 (void) fprintf(stderr,
606 606 gettext("%s: %s is a directory\n"),
607 607 cmd, source);
608 608 return (1);
609 609 }
610 610 (void) fprintf(stderr,
611 611 gettext("%s: cannot rename %s to %s: "),
612 612 cmd, source, target);
613 613 perror("");
614 614 return (1);
615 615 }
616 616
617 617 /*
618 618 * cannot move a non-directory (source) onto an existing
619 619 * directory (target)
620 620 *
621 621 */
622 622 if (targetexists && ISDIR(s2) && (!ISDIR(s1))) {
623 623 (void) fprintf(stderr,
624 624 gettext("%s: cannot mv a non directory %s "
625 625 "over existing directory"
626 626 " %s \n"), cmd, source, target);
627 627 return (1);
628 628 }
629 629 if (ISDIR(s1)) {
630 630 #ifdef XPG4
631 631 if (targetexists && ISDIR(s2)) {
632 632 /* existing target dir must be empty */
633 633 if (rmdir(target) < 0) {
634 634 errno_save = errno;
635 635 (void) fprintf(stderr,
636 636 gettext("%s: cannot rmdir %s: "),
637 637 cmd, target);
638 638 errno = errno_save;
639 639 perror("");
640 640 return (1);
641 641 }
642 642 }
643 643 #endif
644 644 if ((n = copydir(source, target)) == 0)
645 645 (void) rmdir(source);
646 646 return (n);
647 647 }
648 648
649 649 /* doors cannot be moved across filesystems */
650 650 if (ISDOOR(s1)) {
651 651 (void) fprintf(stderr,
652 652 gettext("%s: %s: cannot move door "
653 653 "across file systems\n"), cmd, source);
654 654 return (1);
655 655 }
656 656
657 657 /* sockets cannot be moved across filesystems */
658 658 if (ISSOCK(s1)) {
659 659 (void) fprintf(stderr,
660 660 gettext("%s: %s: cannot move socket "
661 661 "across file systems\n"), cmd, source);
662 662 return (1);
663 663 }
664 664
665 665 /*
666 666 * File cannot be renamed, try to recreate the symbolic
667 667 * link or special device, or copy the file wholesale
668 668 * between file systems.
669 669 */
670 670 if (ISLNK(s1)) {
671 671 register int m;
672 672 register mode_t md;
673 673 char symln[PATH_MAX + 1];
674 674
675 675 if (targetexists && unlink(target) < 0) {
676 676 (void) fprintf(stderr,
677 677 gettext("%s: cannot unlink %s: "),
678 678 cmd, target);
679 679 perror("");
680 680 return (1);
681 681 }
682 682
683 683 if ((m = readlink(source, symln,
684 684 sizeof (symln) - 1)) < 0) {
685 685 Perror(source);
686 686 return (1);
687 687 }
688 688 symln[m] = '\0';
689 689
690 690 md = umask(~(s1.st_mode & MODEBITS));
691 691 if (symlink(symln, target) < 0) {
692 692 Perror(target);
693 693 return (1);
694 694 }
695 695 (void) umask(md);
696 696 m = lchown(target, UID(s1), GID(s1));
697 697 #ifdef XPG4
698 698 if (m < 0) {
699 699 (void) fprintf(stderr, gettext("%s: cannot"
700 700 " change owner and group of"
701 701 " %s: "), cmd, target);
702 702 perror("");
703 703 }
704 704 #endif
705 705 goto cleanup;
706 706 }
707 707 if (ISDEV(s1)) {
708 708
709 709 if (targetexists && unlink(target) < 0) {
710 710 (void) fprintf(stderr,
711 711 gettext("%s: cannot unlink %s: "),
712 712 cmd, target);
713 713 perror("");
714 714 return (1);
715 715 }
716 716
717 717 if (mknod(target, s1.st_mode, s1.st_rdev) < 0) {
718 718 Perror(target);
719 719 return (1);
720 720 }
721 721
722 722 (void) chg_mode(target, UID(s1), GID(s1), FMODE(s1));
723 723 (void) chg_time(target, s1);
724 724 goto cleanup;
725 725 }
726 726
727 727 if (ISREG(s1)) {
728 728 if (ISDIR(s2)) {
729 729 if (targetexists && rmdir(target) < 0) {
730 730 (void) fprintf(stderr,
731 731 gettext("%s: cannot rmdir %s: "),
732 732 cmd, target);
733 733 perror("");
734 734 return (1);
735 735 }
736 736 } else {
737 737 if (targetexists && unlink(target) < 0) {
738 738 (void) fprintf(stderr,
739 739 gettext("%s: cannot unlink %s: "),
740 740 cmd, target);
741 741 perror("");
742 742 return (1);
743 743 }
744 744 }
745 745
746 746
747 747 copy:
748 748 /*
749 749 * If the source file is a symlink, and either
750 750 * -P or -H flag (only if -H is specified and the
751 751 * source file is not a command line argument)
752 752 * were specified, then action is taken on the symlink
753 753 * itself, not the file referenced by the symlink.
754 754 * Note: this is executed for 'cp' only.
755 755 */
756 756 if (cpy && (Pflg || (Hflg && !cmdarg)) && (ISLNK(s1))) {
757 757 int m;
758 758 mode_t md;
759 759 char symln[PATH_MAX + 1];
760 760
761 761 m = readlink(source, symln, sizeof (symln) - 1);
762 762
763 763 if (m < 0) {
764 764 Perror(source);
765 765 return (1);
766 766 }
767 767 symln[m] = '\0';
768 768
769 769 /*
770 770 * Copy the sym link to the target.
771 771 * Note: If the target exists, write a
772 772 * diagnostic message, do nothing more
773 773 * with the source file, and return to
774 774 * process any remaining files.
775 775 */
776 776 md = umask(~(s1.st_mode & MODEBITS));
777 777 if (symlink(symln, target) < 0) {
778 778 Perror(target);
779 779 return (1);
780 780 }
781 781 (void) umask(md);
782 782 m = lchown(target, UID(s1), GID(s1));
783 783
784 784 if (m < 0) {
785 785 (void) fprintf(stderr, gettext(
786 786 "cp: cannot change owner and "
787 787 "group of %s:"), target);
788 788 perror("");
789 789 }
790 790 } else {
791 791 /*
792 792 * Copy the file. If it happens to be a
793 793 * symlink, copy the file referenced
794 794 * by the symlink.
795 795 */
796 796 fi = open(source, O_RDONLY);
797 797 if (fi < 0) {
798 798 (void) fprintf(stderr,
799 799 gettext("%s: cannot open %s: "),
800 800 cmd, source);
801 801 perror("");
802 802 return (1);
803 803 }
804 804
805 805 fo = creat(target, s1.st_mode & MODEBITS);
806 806 if (fo < 0) {
807 807 /*
808 808 * If -f and creat() failed, unlink
809 809 * and try again.
810 810 */
811 811 if (fflg) {
812 812 (void) unlink(target);
813 813 fo = creat(target,
814 814 s1.st_mode & MODEBITS);
815 815 }
816 816 }
817 817 if (fo < 0) {
818 818 (void) fprintf(stderr,
819 819 gettext("%s: cannot create %s: "),
820 820 cmd, target);
821 821 perror("");
822 822 (void) close(fi);
823 823 return (1);
824 824 } else {
825 825 /* stat the new file, its used below */
826 826 (void) stat(target, &s2);
827 827 }
828 828
829 829 /*
830 830 * Set target's permissions to the source
831 831 * before any copying so that any partially
832 832 * copied file will have the source's
833 833 * permissions (at most) or umask permissions
834 834 * whichever is the most restrictive.
835 835 *
836 836 * ACL for regular files
837 837 */
838 838
839 839 if (pflg || mve) {
840 840 (void) chmod(target, FMODE(s1));
841 841 if (s1acl != NULL) {
842 842 if ((acl_set(target,
843 843 s1acl)) < 0) {
844 844 error++;
845 845 (void) fprintf(stderr,
846 846 gettext("%s: "
847 847 "Failed to set "
848 848 "acl entries "
849 849 "on %s\n"), cmd,
850 850 target);
851 851 acl_free(s1acl);
852 852 s1acl = NULL;
853 853 /*
854 854 * else: silent and
855 855 * continue
856 856 */
857 857 }
858 858 }
859 859 }
860 860
861 861 if (fstat(fi, &s1) < 0) {
862 862 (void) fprintf(stderr,
863 863 gettext("%s: cannot access %s\n"),
864 864 cmd, source);
865 865 return (1);
866 866 }
867 867 if (IDENTICAL(s1, s2)) {
868 868 (void) fprintf(stderr,
869 869 gettext(
870 870 "%s: %s and %s are identical\n"),
871 871 cmd, source, target);
872 872 return (1);
873 873 }
874 874
875 875 if (writefile(fi, fo, source, target, NULL,
876 876 NULL, &s1, &s2) != 0) {
877 877 return (1);
878 878 }
879 879
880 880 (void) close(fi);
881 881 if (close(fo) < 0) {
882 882 Perror2(target, "write");
883 883 return (1);
884 884 }
885 885 }
886 886 /* Copy regular extended attributes */
887 887 if (pflg || atflg || mve || saflg) {
888 888 attret = copyattributes(source, target);
889 889 if (attret != 0 && !attrsilent) {
890 890 (void) fprintf(stderr, gettext(
891 891 "%s: Failed to preserve"
892 892 " extended attributes of file"
893 893 " %s\n"), cmd, source);
894 894 }
895 895 /* Copy extended system attributes */
896 896 if (pflg || mve || saflg)
897 897 sattret = copy_sysattr(source, target);
898 898 if (mve && attret != 0) {
899 899 (void) unlink(target);
900 900 return (1);
901 901 }
902 902 if (attrsilent) {
903 903 attret = 0;
904 904 }
905 905 }
906 906
907 907 /*
908 908 * XPG4: the write system call will clear setgid
909 909 * and setuid bits, so set them again.
910 910 */
911 911 if (pflg || mve) {
912 912 if ((ret = chg_mode(target, UID(s1), GID(s1),
913 913 FMODE(s1))) > 0)
914 914 return (1);
915 915 /*
916 916 * Reapply ACL, since chmod may have
917 917 * altered ACL
918 918 */
919 919 if (s1acl != NULL) {
920 920 if ((acl_set(target, s1acl)) < 0) {
921 921 error++;
922 922 (void) fprintf(stderr,
923 923 gettext("%s: Failed to "
924 924 "set acl entries "
925 925 "on %s\n"), cmd, target);
926 926 /*
927 927 * else: silent and
928 928 * continue
929 929 */
930 930 }
931 931 }
932 932 if ((ret = chg_time(target, s1)) > 0)
933 933 return (1);
934 934 }
935 935 if (cpy) {
936 936 if (error != 0 || attret != 0 || sattret != 0)
937 937 return (1);
938 938 return (0);
939 939 }
940 940 goto cleanup;
941 941 }
942 942 (void) fprintf(stderr,
943 943 gettext("%s: %s: unknown file type 0x%x\n"), cmd,
944 944 source, (s1.st_mode & S_IFMT));
945 945 return (1);
946 946
947 947 cleanup:
948 948 if (unlink(source) < 0) {
949 949 (void) unlink(target);
950 950 (void) fprintf(stderr,
951 951 gettext("%s: cannot unlink %s: "),
952 952 cmd, source);
953 953 perror("");
954 954 return (1);
955 955 }
956 956 if (error != 0 || attret != 0 || sattret != 0)
957 957 return (1);
958 958 return (ret);
959 959 }
960 960 /*NOTREACHED*/
961 961 return (ret);
962 962 }
963 963
964 964 /*
965 965 * create_tnode()
966 966 *
967 967 * Create a node for use with the search tree which contains the
968 968 * inode information (device id and inode number).
969 969 *
970 970 * Input
971 971 * dev - device id
972 972 * ino - inode number
973 973 *
974 974 * Output
975 975 * tnode - NULL on error, otherwise returns a tnode structure
976 976 * which contains the input device id and inode number.
977 977 */
978 978 static tree_node_t *
979 979 create_tnode(dev_t dev, ino_t ino)
980 980 {
981 981 tree_node_t *tnode;
982 982
983 983 if ((tnode = (tree_node_t *)malloc(sizeof (tree_node_t))) != NULL) {
984 984 tnode->node_dev = dev;
985 985 tnode->node_ino = ino;
986 986 }
987 987
988 988 return (tnode);
989 989 }
990 990
991 991 static int
992 992 chkfiles(char *source, char **to)
993 993 {
994 994 char *buf = (char *)NULL;
995 995 int (*statf)() = (cpy &&
996 996 !(Pflg || (Hflg && !cmdarg))) ? stat : lstat;
997 997 char *target = *to;
998 998 int error;
999 999
1000 1000 /*
1001 1001 * Make sure source file exists.
1002 1002 */
1003 1003 if ((*statf)(source, &s1) < 0) {
1004 1004 /*
1005 1005 * Keep the old error message except when someone tries to
1006 1006 * mv/cp/ln a symbolic link that has a trailing slash and
1007 1007 * points to a file.
1008 1008 */
1009 1009 if (errno == ENOTDIR)
1010 1010 (void) fprintf(stderr, "%s: %s: %s\n", cmd, source,
1011 1011 strerror(errno));
1012 1012 else
1013 1013 (void) fprintf(stderr,
1014 1014 gettext("%s: cannot access %s\n"), cmd, source);
1015 1015 return (1);
1016 1016 }
1017 1017
1018 1018 /*
1019 1019 * Get ACL info: don't bother with ln or cp/mv'ing symlinks
1020 1020 */
1021 1021 if (!lnk && !ISLNK(s1)) {
1022 1022 if (s1acl != NULL) {
1023 1023 acl_free(s1acl);
1024 1024 s1acl = NULL;
1025 1025 }
1026 1026 if ((error = acl_get(source, ACL_NO_TRIVIAL, &s1acl)) != 0) {
1027 1027 (void) fprintf(stderr,
1028 1028 "%s: failed to get acl entries: %s\n", source,
1029 1029 acl_strerror(error));
1030 1030 return (1);
1031 1031 }
1032 1032 /* else: just permission bits */
1033 1033 }
1034 1034
1035 1035 /*
1036 1036 * If stat fails, then the target doesn't exist,
1037 1037 * we will create a new target with default file type of regular.
1038 1038 */
1039 1039
1040 1040 FTYPE(s2) = S_IFREG;
1041 1041 targetexists = 0;
1042 1042 if ((*statf)(target, &s2) >= 0) {
1043 1043 if (ISLNK(s2))
1044 1044 (void) stat(target, &s2);
1045 1045 /*
1046 1046 * If target is a directory,
1047 1047 * make complete name of new file
1048 1048 * within that directory.
1049 1049 */
1050 1050 if (ISDIR(s2)) {
1051 1051 size_t len;
1052 1052
1053 1053 len = strlen(target) + strlen(dname(source)) + 4;
1054 1054 if ((buf = (char *)malloc(len)) == NULL) {
1055 1055 (void) fprintf(stderr,
1056 1056 gettext("%s: Insufficient memory to "
1057 1057 "%s %s\n "), cmd, cmd, source);
1058 1058 exit(3);
1059 1059 }
1060 1060 (void) snprintf(buf, len, "%s/%s",
1061 1061 target, dname(source));
1062 1062 *to = target = buf;
1063 1063 }
1064 1064
1065 1065 if ((*statf)(target, &s2) >= 0) {
1066 1066 int overwrite = FALSE;
1067 1067 int override = FALSE;
1068 1068
1069 1069 targetexists++;
1070 1070 if (cpy || mve) {
1071 1071 /*
1072 1072 * For cp and mv, it is an error if the
1073 1073 * source and target are the same file.
1074 1074 * Check for the same inode and file
1075 1075 * system, but don't check for the same
1076 1076 * absolute pathname because it is an
1077 1077 * error when the source and target are
1078 1078 * hard links to the same file.
1079 1079 */
1080 1080 if (IDENTICAL(s1, s2)) {
1081 1081 (void) fprintf(stderr,
1082 1082 gettext(
1083 1083 "%s: %s and %s are identical\n"),
1084 1084 cmd, source, target);
1085 1085 if (buf != NULL)
1086 1086 free(buf);
1087 1087 return (1);
1088 1088 }
1089 1089 }
1090 1090 if (lnk) {
1091 1091 /*
1092 1092 * For ln, it is an error if the source and
1093 1093 * target are identical files (same inode,
1094 1094 * same file system, and filenames resolve
1095 1095 * to same absolute pathname).
1096 1096 */
1097 1097 if (!chk_different(source, target)) {
1098 1098 if (buf != NULL)
1099 1099 free(buf);
1100 1100 return (1);
1101 1101 }
1102 1102 }
1103 1103 if (lnk && !silent) {
1104 1104 (void) fprintf(stderr,
1105 1105 gettext("%s: %s: File exists\n"),
1106 1106 cmd, target);
1107 1107 if (buf != NULL)
1108 1108 free(buf);
1109 1109 return (1);
1110 1110 }
1111 1111
1112 1112 /*
1113 1113 * overwrite:
1114 1114 * If the user does not have access to
1115 1115 * the target, ask ----if it is not
1116 1116 * silent and user invoked command
1117 1117 * interactively.
1118 1118 *
1119 1119 * override:
1120 1120 * If not silent, and stdin is a terminal, and
1121 1121 * there's no write access, and the file isn't a
1122 1122 * symbolic link, ask for permission.
1123 1123 *
1124 1124 * XPG4: both overwrite and override:
1125 1125 * ask only one question.
1126 1126 *
1127 1127 * TRANSLATION_NOTE - The following messages will
1128 1128 * contain the first character of the strings for
1129 1129 * "yes" and "no" defined in the file
1130 1130 * "nl_langinfo.po". After substitution, the
1131 1131 * message will appear as follows:
1132 1132 * <cmd>: overwrite <filename> (y/n)?
1133 1133 * where <cmd> is the name of the command
1134 1134 * (cp, mv) and <filename> is the destination file
1135 1135 */
1136 1136
1137 1137
1138 1138 overwrite = iflg && !silent && use_stdin();
1139 1139 override = !cpy && (access(target, 2) < 0) &&
1140 1140 !silent && use_stdin() && !ISLNK(s2);
1141 1141
1142 1142 if (overwrite && override) {
1143 1143 (void) fprintf(stderr,
1144 1144 gettext("%s: overwrite %s and override "
1145 1145 "protection %o (%s/%s)? "), cmd, target,
1146 1146 FMODE(s2) & MODEBITS, yesstr, nostr);
1147 1147 if (yes() == 0) {
1148 1148 if (buf != NULL)
1149 1149 free(buf);
1150 1150 return (2);
1151 1151 }
1152 1152 } else if (overwrite && ISREG(s2)) {
1153 1153 (void) fprintf(stderr,
1154 1154 gettext("%s: overwrite %s (%s/%s)? "),
1155 1155 cmd, target, yesstr, nostr);
1156 1156 if (yes() == 0) {
1157 1157 if (buf != NULL)
1158 1158 free(buf);
1159 1159 return (2);
1160 1160 }
1161 1161 } else if (override) {
1162 1162 (void) fprintf(stderr,
1163 1163 gettext("%s: %s: override protection "
1164 1164 /*CSTYLED*/
1165 1165 "%o (%s/%s)? "),
1166 1166 /*CSTYLED*/
1167 1167 cmd, target, FMODE(s2) & MODEBITS,
1168 1168 yesstr, nostr);
1169 1169 if (yes() == 0) {
1170 1170 if (buf != NULL)
1171 1171 free(buf);
1172 1172 return (2);
1173 1173 }
1174 1174 }
1175 1175
1176 1176 if (lnk && unlink(target) < 0) {
1177 1177 (void) fprintf(stderr,
1178 1178 gettext("%s: cannot unlink %s: "),
1179 1179 cmd, target);
1180 1180 perror("");
1181 1181 return (1);
1182 1182 }
1183 1183 }
1184 1184 }
1185 1185 return (0);
1186 1186 }
1187 1187
1188 1188 /*
1189 1189 * check whether source and target are different
1190 1190 * return 1 when they are different
1191 1191 * return 0 when they are identical, or when unable to resolve a pathname
1192 1192 */
1193 1193 static int
1194 1194 chk_different(char *source, char *target)
1195 1195 {
1196 1196 char rtarget[PATH_MAX], rsource[PATH_MAX];
1197 1197
1198 1198 if (IDENTICAL(s1, s2)) {
1199 1199 /*
1200 1200 * IDENTICAL will be true for hard links, therefore
1201 1201 * check whether the filenames are different
1202 1202 */
1203 1203 if ((getrealpath(source, rsource) == 0) ||
1204 1204 (getrealpath(target, rtarget) == 0)) {
1205 1205 return (0);
1206 1206 }
1207 1207 if (strncmp(rsource, rtarget, PATH_MAX) == 0) {
1208 1208 (void) fprintf(stderr, gettext(
1209 1209 "%s: %s and %s are identical\n"),
1210 1210 cmd, source, target);
1211 1211 return (0);
1212 1212 }
1213 1213 }
1214 1214 return (1);
1215 1215 }
1216 1216
1217 1217 /*
1218 1218 * get real path (resolved absolute pathname)
1219 1219 * return 1 on success, 0 on failure
1220 1220 */
1221 1221 static int
1222 1222 getrealpath(char *path, char *rpath)
1223 1223 {
1224 1224 if (realpath(path, rpath) == NULL) {
1225 1225 int errno_save = errno;
1226 1226 (void) fprintf(stderr, gettext(
1227 1227 "%s: cannot resolve path %s: "), cmd, path);
1228 1228 errno = errno_save;
1229 1229 perror("");
1230 1230 return (0);
1231 1231 }
1232 1232 return (1);
1233 1233 }
1234 1234
1235 1235 static int
1236 1236 rcopy(char *from, char *to)
1237 1237 {
1238 1238 DIR *fold = opendir(from);
1239 1239 struct dirent *dp;
1240 1240 struct stat statb, s1save;
1241 1241 int errs = 0;
1242 1242 char fromname[PATH_MAX];
1243 1243
1244 1244 if (fold == 0 || ((pflg || mve) && fstat(fold->dd_fd, &statb) < 0)) {
1245 1245 Perror(from);
1246 1246 return (1);
1247 1247 }
1248 1248 if (pflg || mve) {
1249 1249 /*
1250 1250 * Save s1 (stat information for source dir) so that
1251 1251 * mod and access times can be reserved during "cp -p"
1252 1252 * or mv, since s1 gets overwritten.
1253 1253 */
1254 1254 s1save = s1;
1255 1255 }
1256 1256 for (;;) {
1257 1257 dp = readdir(fold);
1258 1258 if (dp == 0) {
1259 1259 (void) closedir(fold);
1260 1260 if (pflg || mve)
1261 1261 return (chg_time(to, s1save) + errs);
1262 1262 return (errs);
1263 1263 }
1264 1264 if (dp->d_ino == 0)
1265 1265 continue;
1266 1266 if ((strcmp(dp->d_name, ".") == 0) ||
1267 1267 (strcmp(dp->d_name, "..") == 0))
1268 1268 continue;
1269 1269 if (strlen(from)+1+strlen(dp->d_name) >=
1270 1270 sizeof (fromname) - 1) {
1271 1271 (void) fprintf(stderr,
1272 1272 gettext("%s : %s/%s: Name too long\n"),
1273 1273 cmd, from, dp->d_name);
1274 1274 errs++;
1275 1275 continue;
1276 1276 }
1277 1277 (void) snprintf(fromname, sizeof (fromname),
1278 1278 "%s/%s", from, dp->d_name);
1279 1279 errs += cpymve(fromname, to);
1280 1280 }
1281 1281 }
1282 1282
1283 1283 static char *
1284 1284 dname(char *name)
1285 1285 {
1286 1286 register char *p;
1287 1287
1288 1288 /*
1289 1289 * Return just the file name given the complete path.
1290 1290 * Like basename(1).
1291 1291 */
1292 1292
1293 1293 p = name;
1294 1294
1295 1295 /*
1296 1296 * While there are characters left,
1297 1297 * set name to start after last
1298 1298 * delimiter.
1299 1299 */
1300 1300
1301 1301 while (*p)
1302 1302 if (*p++ == DELIM && *p)
1303 1303 name = p;
1304 1304 return (name);
1305 1305 }
1306 1306
1307 1307 static void
1308 1308 usage(void)
1309 1309 {
1310 1310 /*
1311 1311 * Display usage message.
1312 1312 */
1313 1313
1314 1314 if (mve) {
1315 1315 (void) fprintf(stderr, gettext(
1316 1316 "Usage: mv [-f] [-i] f1 f2\n"
1317 1317 " mv [-f] [-i] f1 ... fn d1\n"
1318 1318 " mv [-f] [-i] d1 d2\n"));
1319 1319 } else if (lnk) {
1320 1320 #ifdef XPG4
1321 1321 (void) fprintf(stderr, gettext(
1322 1322 "Usage: ln [-f] [-s] f1 [f2]\n"
1323 1323 " ln [-f] [-s] f1 ... fn d1\n"
1324 1324 " ln [-f] -s d1 d2\n"));
1325 1325 #else
1326 1326 (void) fprintf(stderr, gettext(
1327 1327 "Usage: ln [-f] [-n] [-s] f1 [f2]\n"
1328 1328 " ln [-f] [-n] [-s] f1 ... fn d1\n"
1329 1329 " ln [-f] [-n] -s d1 d2\n"));
1330 1330 #endif
1331 1331 } else if (cpy) {
1332 1332 (void) fprintf(stderr, gettext(
1333 1333 "Usage: cp [-a] [-f] [-i] [-p] [-@] [-/] f1 f2\n"
1334 1334 " cp [-a] [-f] [-i] [-p] [-@] [-/] f1 ... fn d1\n"
1335 1335 " cp [-r|-R [-H|-L|-P]] [-a] [-f] [-i] [-p] [-@] "
1336 1336 "[-/] d1 ... dn-1 dn\n"));
1337 1337 }
1338 1338 exit(2);
1339 1339 }
1340 1340
1341 1341 /*
1342 1342 * chg_time()
1343 1343 *
1344 1344 * Try to preserve modification and access time.
1345 1345 * If 1) pflg is not set, or 2) pflg is set and this is the Solaris version,
1346 1346 * don't report a utimensat() failure.
1347 1347 * If this is the XPG4 version and utimensat fails, if 1) pflg is set (cp -p)
1348 1348 * or 2) we are doing a mv, print a diagnostic message; arrange for a non-zero
1349 1349 * exit status only if pflg is set.
1350 1350 * utimensat(2) is being used to achieve granularity in nanoseconds
1351 1351 * (if supported by the underlying file system) while setting file times.
1352 1352 */
1353 1353 static int
1354 1354 chg_time(char *to, struct stat ss)
1355 1355 {
1356 1356 struct timespec times[2];
1357 1357 int rc;
1358 1358
1359 1359 times[0] = ss.st_atim;
1360 1360 times[1] = ss.st_mtim;
1361 1361
1362 1362 rc = utimensat(AT_FDCWD, to, times,
1363 1363 ISLNK(s1) ? AT_SYMLINK_NOFOLLOW : 0);
1364 1364 #ifdef XPG4
1365 1365 if ((pflg || mve) && rc != 0) {
1366 1366 (void) fprintf(stderr,
1367 1367 gettext("%s: cannot set times for %s: "), cmd, to);
1368 1368 perror("");
1369 1369 if (pflg)
1370 1370 return (1);
1371 1371 }
1372 1372 #endif
1373 1373
1374 1374 return (0);
1375 1375
1376 1376 }
1377 1377
1378 1378 /*
1379 1379 * chg_mode()
1380 1380 *
1381 1381 * This function is called upon "cp -p" or mv across filesystems.
1382 1382 *
1383 1383 * Try to preserve the owner and group id. If chown() fails,
1384 1384 * only print a diagnostic message if doing a mv in the XPG4 version;
1385 1385 * try to clear S_ISUID and S_ISGID bits in the target. If unable to clear
1386 1386 * S_ISUID and S_ISGID bits, print a diagnostic message and arrange for a
1387 1387 * non-zero exit status because this is a security violation.
1388 1388 * Try to preserve permissions.
1389 1389 * If this is the XPG4 version and chmod() fails, print a diagnostic message
1390 1390 * and arrange for a non-zero exit status.
1391 1391 * If this is the Solaris version and chmod() fails, do not print a
1392 1392 * diagnostic message or exit with a non-zero value.
1393 1393 */
1394 1394 static int
1395 1395 chg_mode(char *target, uid_t uid, gid_t gid, mode_t mode)
1396 1396 {
1397 1397 int clearflg = 0; /* controls message printed upon chown() error */
1398 1398 struct stat st;
1399 1399
1400 1400 /* Don't change mode if target is symlink */
1401 1401 if (lstat(target, &st) == 0 && ISLNK(st))
1402 1402 return (0);
1403 1403
1404 1404 if (chown(target, uid, gid) != 0) {
1405 1405 #ifdef XPG4
1406 1406 if (mve) {
1407 1407 (void) fprintf(stderr, gettext("%s: cannot change"
1408 1408 " owner and group of %s: "), cmd, target);
1409 1409 perror("");
1410 1410 }
1411 1411 #endif
1412 1412 if (mode & (S_ISUID | S_ISGID)) {
1413 1413 /* try to clear S_ISUID and S_ISGID */
1414 1414 mode &= ~S_ISUID & ~S_ISGID;
1415 1415 ++clearflg;
1416 1416 }
1417 1417 }
1418 1418 if (chmod(target, mode) != 0) {
1419 1419 if (clearflg) {
1420 1420 (void) fprintf(stderr, gettext(
1421 1421 "%s: cannot clear S_ISUID and S_ISGID bits in"
1422 1422 " %s: "), cmd, target);
1423 1423 perror("");
1424 1424 /* cp -p should get non-zero exit; mv should not */
1425 1425 if (pflg)
1426 1426 return (1);
1427 1427 }
1428 1428 #ifdef XPG4
1429 1429 else {
1430 1430 (void) fprintf(stderr, gettext(
1431 1431 "%s: cannot set permissions for %s: "), cmd, target);
1432 1432 perror("");
1433 1433 /* cp -p should get non-zero exit; mv should not */
1434 1434 if (pflg)
1435 1435 return (1);
1436 1436 }
1437 1437 #endif
1438 1438 }
1439 1439 return (0);
1440 1440
1441 1441 }
1442 1442
1443 1443 static void
1444 1444 Perror(char *s)
1445 1445 {
1446 1446 char buf[PATH_MAX + 10];
1447 1447
1448 1448 (void) snprintf(buf, sizeof (buf), "%s: %s", cmd, s);
1449 1449 perror(buf);
1450 1450 }
1451 1451
1452 1452 static void
1453 1453 Perror2(char *s1, char *s2)
1454 1454 {
1455 1455 char buf[PATH_MAX + 20];
1456 1456
1457 1457 (void) snprintf(buf, sizeof (buf), "%s: %s: %s",
1458 1458 cmd, gettext(s1), gettext(s2));
1459 1459 perror(buf);
1460 1460 }
1461 1461
1462 1462 /*
1463 1463 * used for cp -R and for mv across file systems
1464 1464 */
1465 1465 static int
1466 1466 copydir(char *source, char *target)
1467 1467 {
1468 1468 int ret, attret = 0;
1469 1469 int sattret = 0;
1470 1470 int pret = 0; /* need separate flag if -p is specified */
1471 1471 mode_t fixmode = (mode_t)0; /* cleanup mode after copy */
1472 1472 struct stat s1save;
1473 1473 acl_t *s1acl_save;
1474 1474 int error = 0;
1475 1475
1476 1476 s1acl_save = NULL;
1477 1477
1478 1478 if (cpy && !rflg) {
1479 1479 (void) fprintf(stderr,
1480 1480 gettext("%s: %s: is a directory\n"), cmd, source);
1481 1481 return (1);
1482 1482 }
1483 1483
1484 1484 if (stat(target, &s2) < 0) {
1485 1485 if (mkdir(target, (s1.st_mode & MODEBITS)) < 0) {
1486 1486 (void) fprintf(stderr, "%s: ", cmd);
1487 1487 perror(target);
1488 1488 return (1);
1489 1489 }
1490 1490 if (stat(target, &s2) == 0) {
1491 1491 fixmode = s2.st_mode;
1492 1492 } else {
1493 1493 fixmode = s1.st_mode;
1494 1494 }
1495 1495 (void) chmod(target, ((fixmode & MODEBITS) | S_IRWXU));
1496 1496 } else if (!(ISDIR(s2))) {
1497 1497 (void) fprintf(stderr,
1498 1498 gettext("%s: %s: not a directory.\n"), cmd, target);
1499 1499 return (1);
1500 1500 }
1501 1501 if (pflg || mve) {
1502 1502 /*
1503 1503 * Save s1 (stat information for source dir) and acl info,
1504 1504 * if any, so that ownership, modes, times, and acl's can
1505 1505 * be reserved during "cp -p" or mv.
1506 1506 * s1 gets overwritten when doing the recursive copy.
1507 1507 */
1508 1508 s1save = s1;
1509 1509 if (s1acl != NULL) {
1510 1510 s1acl_save = acl_dup(s1acl);
1511 1511 if (s1acl_save == NULL) {
1512 1512 (void) fprintf(stderr, gettext("%s: "
1513 1513 "Insufficient memory to save acl"
1514 1514 " entry\n"), cmd);
1515 1515 if (pflg)
1516 1516 return (1);
1517 1517
1518 1518 }
1519 1519 #ifdef XPG4
1520 1520 else {
1521 1521 (void) fprintf(stderr, gettext("%s: "
1522 1522 "Insufficient memory to save acl"
1523 1523 " entry\n"), cmd);
1524 1524 if (pflg)
1525 1525 return (1);
1526 1526 }
1527 1527 #endif
1528 1528 }
1529 1529 }
1530 1530
1531 1531 ret = rcopy(source, target);
1532 1532
1533 1533 /*
1534 1534 * Once we created a directory, go ahead and set
1535 1535 * its attributes, e.g. acls and time. The info
1536 1536 * may get overwritten if we continue traversing
1537 1537 * down the tree.
1538 1538 *
1539 1539 * ACL for directory
1540 1540 */
1541 1541 if (pflg || mve) {
1542 1542 if ((pret = chg_mode(target, UID(s1save), GID(s1save),
1543 1543 FMODE(s1save))) == 0)
1544 1544 pret = chg_time(target, s1save);
1545 1545 ret += pret;
1546 1546 if (s1acl_save != NULL) {
1547 1547 if (acl_set(target, s1acl_save) < 0) {
1548 1548 error++;
1549 1549 #ifdef XPG4
1550 1550 if (pflg || mve) {
1551 1551 #else
1552 1552 if (pflg) {
1553 1553 #endif
1554 1554 (void) fprintf(stderr, gettext(
1555 1555 "%s: failed to set acl entries "
1556 1556 "on %s\n"), cmd, target);
1557 1557 if (pflg) {
1558 1558 acl_free(s1acl_save);
1559 1559 s1acl_save = NULL;
1560 1560 ret++;
1561 1561 }
1562 1562 }
1563 1563 /* else: silent and continue */
1564 1564 }
1565 1565 acl_free(s1acl_save);
1566 1566 s1acl_save = NULL;
1567 1567 }
1568 1568 } else if (fixmode != (mode_t)0)
1569 1569 (void) chmod(target, fixmode & MODEBITS);
1570 1570
1571 1571 if (pflg || atflg || mve || saflg) {
1572 1572 attret = copyattributes(source, target);
1573 1573 if (!attrsilent && attret != 0) {
1574 1574 (void) fprintf(stderr, gettext("%s: Failed to preserve"
1575 1575 " extended attributes of directory"
1576 1576 " %s\n"), cmd, source);
1577 1577 } else {
1578 1578 /*
1579 1579 * Otherwise ignore failure.
1580 1580 */
1581 1581 attret = 0;
1582 1582 }
1583 1583 /* Copy extended system attributes */
1584 1584 if (pflg || mve || saflg) {
1585 1585 sattret = copy_sysattr(source, target);
1586 1586 if (sattret != 0) {
1587 1587 (void) fprintf(stderr, gettext(
1588 1588 "%s: Failed to preserve "
1589 1589 "extended system attributes "
1590 1590 "of directory %s\n"), cmd, source);
1591 1591 }
1592 1592 }
1593 1593 }
1594 1594 if (attret != 0 || sattret != 0 || error != 0)
1595 1595 return (1);
1596 1596 return (ret);
1597 1597 }
1598 1598
1599 1599 static int
1600 1600 copyspecial(char *target)
1601 1601 {
1602 1602 int ret = 0;
1603 1603
1604 1604 if (mknod(target, s1.st_mode, s1.st_rdev) != 0) {
1605 1605 (void) fprintf(stderr, gettext(
1606 1606 "cp: cannot create special file %s: "), target);
1607 1607 perror("");
1608 1608 return (1);
1609 1609 }
1610 1610
1611 1611 if (pflg) {
1612 1612 if ((ret = chg_mode(target, UID(s1), GID(s1), FMODE(s1))) == 0)
1613 1613 ret = chg_time(target, s1);
1614 1614 }
1615 1615
1616 1616 return (ret);
1617 1617 }
1618 1618
1619 1619 static int
1620 1620 use_stdin(void)
1621 1621 {
1622 1622 #ifdef XPG4
1623 1623 return (1);
1624 1624 #else
1625 1625 return (isatty(fileno(stdin)));
1626 1626 #endif
1627 1627 }
1628 1628
1629 1629 /* Copy non-system extended attributes */
1630 1630
1631 1631 static int
1632 1632 copyattributes(char *source, char *target)
1633 1633 {
1634 1634 struct dirent *dp;
1635 1635 int error = 0;
1636 1636 int aclerror;
1637 1637 mode_t mode;
1638 1638 int clearflg = 0;
1639 1639 acl_t *xacl = NULL;
1640 1640 acl_t *attrdiracl = NULL;
1641 1641 struct timespec times[2];
1642 1642
1643 1643
1644 1644 if (pathconf(source, _PC_XATTR_EXISTS) != 1)
1645 1645 return (0);
1646 1646
1647 1647 if (pathconf(target, _PC_XATTR_ENABLED) != 1) {
1648 1648 if (!attrsilent) {
1649 1649 (void) fprintf(stderr,
1650 1650 gettext(
1651 1651 "%s: cannot preserve extended attributes, "
1652 1652 "operation not supported on file"
1653 1653 " %s\n"), cmd, target);
1654 1654 }
1655 1655 return (1);
1656 1656 }
1657 1657 if (open_source(source) != 0)
1658 1658 return (1);
1659 1659 if (open_target_srctarg_attrdirs(source, target) != 0)
1660 1660 return (1);
1661 1661 if (open_attrdirp(source) != 0)
1662 1662 return (1);
1663 1663
1664 1664 if (pflg || mve) {
1665 1665 if (fchmod(targetdirfd, attrdir.st_mode) == -1) {
1666 1666 if (!attrsilent) {
1667 1667 (void) fprintf(stderr,
1668 1668 gettext("%s: failed to set file mode"
1669 1669 " correctly on attribute directory of"
1670 1670 " file %s: "), cmd, target);
1671 1671 perror("");
1672 1672 ++error;
1673 1673 }
1674 1674 }
1675 1675
1676 1676 if (fchown(targetdirfd, attrdir.st_uid, attrdir.st_gid) == -1) {
1677 1677 if (!attrsilent) {
1678 1678 (void) fprintf(stderr,
1679 1679 gettext("%s: failed to set file"
1680 1680 " ownership correctly on attribute"
1681 1681 " directory of file %s: "), cmd, target);
1682 1682 perror("");
1683 1683 ++error;
1684 1684 }
1685 1685 }
1686 1686 /*
1687 1687 * Now that we are the owner we can update st_ctime by calling
1688 1688 * utimensat.
1689 1689 */
1690 1690 times[0] = attrdir.st_atim;
1691 1691 times[1] = attrdir.st_mtim;
1692 1692 if (utimensat(targetdirfd, ".", times, 0) < 0) {
1693 1693 if (!attrsilent) {
1694 1694 (void) fprintf(stderr,
1695 1695 gettext("%s: cannot set attribute times"
1696 1696 " for %s: "), cmd, target);
1697 1697 perror("");
1698 1698 ++error;
1699 1699 }
1700 1700 }
1701 1701
1702 1702 /*
1703 1703 * Now set owner and group of attribute directory, implies
1704 1704 * changing the ACL of the hidden attribute directory first.
1705 1705 */
1706 1706 if ((aclerror = facl_get(sourcedirfd,
1707 1707 ACL_NO_TRIVIAL, &attrdiracl)) != 0) {
1708 1708 if (!attrsilent) {
1709 1709 (void) fprintf(stderr, gettext(
1710 1710 "%s: failed to get acl entries of"
1711 1711 " attribute directory for"
1712 1712 " %s : %s\n"), cmd,
1713 1713 source, acl_strerror(aclerror));
1714 1714 ++error;
1715 1715 }
1716 1716 }
1717 1717
1718 1718 if (attrdiracl) {
1719 1719 if (facl_set(targetdirfd, attrdiracl) != 0) {
1720 1720 if (!attrsilent) {
1721 1721 (void) fprintf(stderr, gettext(
1722 1722 "%s: failed to set acl entries"
1723 1723 " on attribute directory "
1724 1724 "for %s\n"), cmd, target);
1725 1725 ++error;
1726 1726 }
1727 1727 acl_free(attrdiracl);
1728 1728 attrdiracl = NULL;
1729 1729 }
1730 1730 }
1731 1731 }
1732 1732
1733 1733 while ((dp = readdir(srcdirp)) != NULL) {
1734 1734 int ret;
1735 1735
1736 1736 if ((ret = traverse_attrfile(dp, source, target, 1)) == -1)
1737 1737 continue;
1738 1738 else if (ret > 0) {
1739 1739 ++error;
1740 1740 goto out;
1741 1741 }
1742 1742
1743 1743 if (pflg || mve) {
1744 1744 if ((aclerror = facl_get(srcattrfd,
1745 1745 ACL_NO_TRIVIAL, &xacl)) != 0) {
1746 1746 if (!attrsilent) {
1747 1747 (void) fprintf(stderr, gettext(
1748 1748 "%s: failed to get acl entries of"
1749 1749 " attribute %s for"
1750 1750 " %s: %s"), cmd, dp->d_name,
1751 1751 source, acl_strerror(aclerror));
1752 1752 ++error;
1753 1753 }
1754 1754 }
1755 1755 }
1756 1756
1757 1757 /*
1758 1758 * preserve ACL
1759 1759 */
1760 1760 if ((pflg || mve) && xacl != NULL) {
1761 1761 if ((facl_set(targattrfd, xacl)) < 0) {
1762 1762 if (!attrsilent) {
1763 1763 (void) fprintf(stderr, gettext(
1764 1764 "%s: failed to set acl entries on"
1765 1765 " attribute %s for"
1766 1766 "%s\n"), cmd, dp->d_name, target);
1767 1767 ++error;
1768 1768 }
1769 1769 acl_free(xacl);
1770 1770 xacl = NULL;
1771 1771 }
1772 1772 }
1773 1773
1774 1774 if (writefile(srcattrfd, targattrfd, source, target,
1775 1775 dp->d_name, dp->d_name, &s3, &s4) != 0) {
1776 1776 if (!attrsilent) {
1777 1777 ++error;
1778 1778 }
1779 1779 goto next;
1780 1780 }
1781 1781
1782 1782 if (pflg || mve) {
1783 1783 mode = FMODE(s3);
1784 1784
1785 1785 if (fchown(targattrfd, UID(s3), GID(s3)) != 0) {
1786 1786 if (!attrsilent) {
1787 1787 (void) fprintf(stderr,
1788 1788 gettext("%s: cannot change"
1789 1789 " owner and group of"
1790 1790 " attribute %s for" " file"
1791 1791 " %s: "), cmd, dp->d_name, target);
1792 1792 perror("");
1793 1793 ++error;
1794 1794 }
1795 1795 if (mode & (S_ISUID | S_ISGID)) {
1796 1796 /* try to clear S_ISUID and S_ISGID */
1797 1797 mode &= ~S_ISUID & ~S_ISGID;
1798 1798 ++clearflg;
1799 1799 }
1800 1800 }
1801 1801 times[0] = s3.st_atim;
1802 1802 times[1] = s3.st_mtim;
1803 1803 if (utimensat(targetdirfd, dp->d_name, times, 0) < 0) {
1804 1804 if (!attrsilent) {
1805 1805 (void) fprintf(stderr,
1806 1806 gettext("%s: cannot set attribute"
1807 1807 " times for %s: "), cmd, target);
1808 1808 perror("");
1809 1809 ++error;
1810 1810 }
1811 1811 }
1812 1812 if (fchmod(targattrfd, mode) != 0) {
1813 1813 if (clearflg) {
1814 1814 (void) fprintf(stderr, gettext(
1815 1815 "%s: cannot clear S_ISUID and "
1816 1816 "S_ISGID bits in attribute %s"
1817 1817 " for file"
1818 1818 " %s: "), cmd, dp->d_name, target);
1819 1819 } else {
1820 1820 if (!attrsilent) {
1821 1821 (void) fprintf(stderr,
1822 1822 gettext(
1823 1823 "%s: cannot set permissions of attribute"
1824 1824 " %s for %s: "), cmd, dp->d_name, target);
1825 1825 perror("");
1826 1826 ++error;
1827 1827 }
1828 1828 }
1829 1829 }
1830 1830 if (xacl && ((facl_set(targattrfd, xacl)) < 0)) {
1831 1831 if (!attrsilent) {
1832 1832 (void) fprintf(stderr, gettext(
1833 1833 "%s: failed to set acl entries on"
1834 1834 " attribute %s for"
1835 1835 "%s\n"), cmd, dp->d_name, target);
1836 1836 ++error;
1837 1837 }
1838 1838 acl_free(xacl);
1839 1839 xacl = NULL;
1840 1840 }
1841 1841 }
1842 1842 next:
1843 1843 if (xacl != NULL) {
1844 1844 acl_free(xacl);
1845 1845 xacl = NULL;
1846 1846 }
1847 1847 if (srcattrfd != -1)
1848 1848 (void) close(srcattrfd);
1849 1849 if (targattrfd != -1)
1850 1850 (void) close(targattrfd);
1851 1851 srcattrfd = targattrfd = -1;
1852 1852 }
1853 1853 out:
1854 1854 if (xacl != NULL) {
1855 1855 acl_free(xacl);
1856 1856 xacl = NULL;
1857 1857 }
1858 1858 if (attrdiracl != NULL) {
1859 1859 acl_free(attrdiracl);
1860 1860 attrdiracl = NULL;
1861 1861 }
1862 1862
1863 1863 if (!saflg && !pflg && !mve)
1864 1864 close_all();
1865 1865 return (error == 0 ? 0 : 1);
1866 1866 }
1867 1867
1868 1868 /* Copy extended system attributes from source to target */
1869 1869
1870 1870 static int
1871 1871 copy_sysattr(char *source, char *target)
1872 1872 {
1873 1873 struct dirent *dp;
1874 1874 nvlist_t *response;
1875 1875 int error = 0;
1876 1876 int target_sa_support = 0;
1877 1877
1878 1878 if (sysattr_support(source, _PC_SATTR_EXISTS) != 1)
1879 1879 return (0);
1880 1880
1881 1881 if (open_source(source) != 0)
1882 1882 return (1);
1883 1883
1884 1884 /*
1885 1885 * Gets non default extended system attributes from the
1886 1886 * source file to copy to the target. The target has
1887 1887 * the defaults set when its created and thus no need
1888 1888 * to copy the defaults.
1889 1889 */
1890 1890 response = sysattr_list(cmd, srcfd, source);
1891 1891
1892 1892 if (sysattr_support(target, _PC_SATTR_ENABLED) != 1) {
1893 1893 if (response != NULL) {
1894 1894 (void) fprintf(stderr,
1895 1895 gettext(
1896 1896 "%s: cannot preserve extended system "
1897 1897 "attribute, operation not supported on file"
1898 1898 " %s\n"), cmd, target);
1899 1899 error++;
1900 1900 goto out;
1901 1901 }
1902 1902 } else {
1903 1903 target_sa_support = 1;
1904 1904 }
1905 1905
1906 1906 if (target_sa_support) {
1907 1907 if (srcdirp == NULL) {
1908 1908 if (open_target_srctarg_attrdirs(source,
1909 1909 target) != 0) {
1910 1910 error++;
1911 1911 goto out;
1912 1912 }
1913 1913 if (open_attrdirp(source) != 0) {
1914 1914 error++;
1915 1915 goto out;
1916 1916 }
1917 1917 } else {
1918 1918 rewind_attrdir(srcdirp);
1919 1919 }
1920 1920 while ((dp = readdir(srcdirp)) != NULL) {
1921 1921 nvlist_t *res;
1922 1922 int ret;
1923 1923
1924 1924 if ((ret = traverse_attrfile(dp, source, target,
1925 1925 0)) == -1)
1926 1926 continue;
1927 1927 else if (ret > 0) {
1928 1928 ++error;
1929 1929 goto out;
1930 1930 }
1931 1931 /*
1932 1932 * Gets non default extended system attributes from the
1933 1933 * attribute file to copy to the target. The target has
1934 1934 * the defaults set when its created and thus no need
1935 1935 * to copy the defaults.
1936 1936 */
1937 1937 if (dp->d_name != NULL) {
1938 1938 res = sysattr_list(cmd, srcattrfd, dp->d_name);
1939 1939 if (res == NULL)
1940 1940 goto next;
1941 1941
1942 1942 /*
1943 1943 * Copy non default extended system attributes of named
1944 1944 * attribute file.
1945 1945 */
1946 1946 if (fsetattr(targattrfd,
1947 1947 XATTR_VIEW_READWRITE, res) != 0) {
1948 1948 ++error;
1949 1949 (void) fprintf(stderr, gettext("%s: "
1950 1950 "Failed to copy extended system "
1951 1951 "attributes from attribute file "
↓ open down ↓ |
1951 lines elided |
↑ open up ↑ |
1952 1952 "%s of %s to %s\n"), cmd,
1953 1953 dp->d_name, source, target);
1954 1954 }
1955 1955 }
1956 1956 next:
1957 1957 if (srcattrfd != -1)
1958 1958 (void) close(srcattrfd);
1959 1959 if (targattrfd != -1)
1960 1960 (void) close(targattrfd);
1961 1961 srcattrfd = targattrfd = -1;
1962 - if (res != NULL)
1963 - nvlist_free(res);
1962 + nvlist_free(res);
1964 1963 }
1965 1964 }
1966 1965 /* Copy source file non default extended system attributes to target */
1967 1966 if (target_sa_support && (response != NULL) &&
1968 1967 (fsetattr(targfd, XATTR_VIEW_READWRITE, response)) != 0) {
1969 1968 ++error;
1970 1969 (void) fprintf(stderr, gettext("%s: Failed to "
1971 1970 "copy extended system attributes from "
1972 1971 "%s to %s\n"), cmd, source, target);
1973 1972 }
1974 1973 out:
1975 - if (response != NULL)
1976 - nvlist_free(response);
1974 + nvlist_free(response);
1977 1975 close_all();
1978 1976 return (error == 0 ? 0 : 1);
1979 1977 }
1980 1978
1981 1979 /* Open the source file */
1982 1980
1983 1981 int
1984 1982 open_source(char *src)
1985 1983 {
1986 1984 int error = 0;
1987 1985
1988 1986 srcfd = -1;
1989 1987 if ((srcfd = open(src, O_RDONLY)) == -1) {
1990 1988 if (pflg && attrsilent) {
1991 1989 error++;
1992 1990 goto out;
1993 1991 }
1994 1992 if (!attrsilent) {
1995 1993 (void) fprintf(stderr,
1996 1994 gettext("%s: cannot open file"
1997 1995 " %s: "), cmd, src);
1998 1996 perror("");
1999 1997 }
2000 1998 ++error;
2001 1999 }
2002 2000 out:
2003 2001 if (error)
2004 2002 close_all();
2005 2003 return (error == 0 ? 0 : 1);
2006 2004 }
2007 2005
2008 2006 /* Open source attribute dir, target and target attribute dir. */
2009 2007
2010 2008 int
2011 2009 open_target_srctarg_attrdirs(char *src, char *targ)
2012 2010 {
2013 2011 int error = 0;
2014 2012
2015 2013 targfd = sourcedirfd = targetdirfd = -1;
2016 2014
2017 2015 if ((targfd = open(targ, O_RDONLY)) == -1) {
2018 2016 if (pflg && attrsilent) {
2019 2017 error++;
2020 2018 goto out;
2021 2019 }
2022 2020 if (!attrsilent) {
2023 2021 (void) fprintf(stderr,
2024 2022 gettext("%s: cannot open file"
2025 2023 " %s: "), cmd, targ);
2026 2024 perror("");
2027 2025 }
2028 2026 ++error;
2029 2027 goto out;
2030 2028 }
2031 2029
2032 2030 if ((sourcedirfd = openat(srcfd, ".", O_RDONLY|O_XATTR)) == -1) {
2033 2031 if (pflg && attrsilent) {
2034 2032 error++;
2035 2033 goto out;
2036 2034 }
2037 2035 if (!attrsilent) {
2038 2036 (void) fprintf(stderr,
2039 2037 gettext("%s: cannot open attribute"
2040 2038 " directory for %s: "), cmd, src);
2041 2039 perror("");
2042 2040 }
2043 2041 ++error;
2044 2042 goto out;
2045 2043 }
2046 2044
2047 2045 if (fstat(sourcedirfd, &attrdir) == -1) {
2048 2046 if (pflg && attrsilent) {
2049 2047 error++;
2050 2048 goto out;
2051 2049 }
2052 2050
2053 2051 if (!attrsilent) {
2054 2052 (void) fprintf(stderr,
2055 2053 gettext("%s: could not retrieve stat"
2056 2054 " information for attribute directory"
2057 2055 "of file %s: "), cmd, src);
2058 2056 perror("");
2059 2057 }
2060 2058 ++error;
2061 2059 goto out;
2062 2060 }
2063 2061 if ((targetdirfd = openat(targfd, ".", O_RDONLY|O_XATTR)) == -1) {
2064 2062 if (pflg && attrsilent) {
2065 2063 error++;
2066 2064 goto out;
2067 2065 }
2068 2066 if (!attrsilent) {
2069 2067 (void) fprintf(stderr,
2070 2068 gettext("%s: cannot open attribute"
2071 2069 " directory for %s: "), cmd, targ);
2072 2070 perror("");
2073 2071 }
2074 2072 ++error;
2075 2073 }
2076 2074 out:
2077 2075 if (error)
2078 2076 close_all();
2079 2077 return (error == 0 ? 0 : 1);
2080 2078 }
2081 2079
2082 2080 int
2083 2081 open_attrdirp(char *source)
2084 2082 {
2085 2083 int tmpfd = -1;
2086 2084 int error = 0;
2087 2085
2088 2086 /*
2089 2087 * dup sourcedirfd for use by fdopendir().
2090 2088 * fdopendir will take ownership of given fd and will close
2091 2089 * it when closedir() is called.
2092 2090 */
2093 2091
2094 2092 if ((tmpfd = dup(sourcedirfd)) == -1) {
2095 2093 if (pflg && attrsilent) {
2096 2094 error++;
2097 2095 goto out;
2098 2096 }
2099 2097 if (!attrsilent) {
2100 2098 (void) fprintf(stderr,
2101 2099 gettext(
2102 2100 "%s: unable to dup attribute directory"
2103 2101 " file descriptor for %s: "), cmd, source);
2104 2102 perror("");
2105 2103 ++error;
2106 2104 }
2107 2105 goto out;
2108 2106 }
2109 2107 if ((srcdirp = fdopendir(tmpfd)) == NULL) {
2110 2108 if (pflg && attrsilent) {
2111 2109 error++;
2112 2110 goto out;
2113 2111 }
2114 2112 if (!attrsilent) {
2115 2113 (void) fprintf(stderr,
2116 2114 gettext("%s: failed to open attribute"
2117 2115 " directory for %s: "), cmd, source);
2118 2116 perror("");
2119 2117 ++error;
2120 2118 }
2121 2119 }
2122 2120 out:
2123 2121 if (error)
2124 2122 close_all();
2125 2123 return (error == 0 ? 0 : 1);
2126 2124 }
2127 2125
2128 2126 /* Skips through ., .., and system attribute 'view' files */
2129 2127 int
2130 2128 traverse_attrfile(struct dirent *dp, char *source, char *target, int first)
2131 2129 {
2132 2130 int error = 0;
2133 2131
2134 2132 srcattrfd = targattrfd = -1;
2135 2133
2136 2134 if ((dp->d_name[0] == '.' && dp->d_name[1] == '\0') ||
2137 2135 (dp->d_name[0] == '.' && dp->d_name[1] == '.' &&
2138 2136 dp->d_name[2] == '\0') ||
2139 2137 (sysattr_type(dp->d_name) == _RO_SATTR) ||
2140 2138 (sysattr_type(dp->d_name) == _RW_SATTR))
2141 2139 return (-1);
2142 2140
2143 2141 if ((srcattrfd = openat(sourcedirfd, dp->d_name,
2144 2142 O_RDONLY)) == -1) {
2145 2143 if (!attrsilent) {
2146 2144 (void) fprintf(stderr,
2147 2145 gettext("%s: cannot open attribute %s on"
2148 2146 " file %s: "), cmd, dp->d_name, source);
2149 2147 perror("");
2150 2148 ++error;
2151 2149 goto out;
2152 2150 }
2153 2151 }
2154 2152
2155 2153 if (fstat(srcattrfd, &s3) < 0) {
2156 2154 if (!attrsilent) {
2157 2155 (void) fprintf(stderr,
2158 2156 gettext("%s: could not stat attribute"
2159 2157 " %s on file"
2160 2158 " %s: "), cmd, dp->d_name, source);
2161 2159 perror("");
2162 2160 ++error;
2163 2161 }
2164 2162 goto out;
2165 2163 }
2166 2164
2167 2165 if (first) {
2168 2166 (void) unlinkat(targetdirfd, dp->d_name, 0);
2169 2167 if ((targattrfd = openat(targetdirfd, dp->d_name,
2170 2168 O_RDWR|O_CREAT|O_TRUNC, s3.st_mode & MODEBITS)) == -1) {
2171 2169 if (!attrsilent) {
2172 2170 (void) fprintf(stderr,
2173 2171 gettext("%s: could not create attribute"
2174 2172 " %s on file %s: "), cmd, dp->d_name,
2175 2173 target);
2176 2174 perror("");
2177 2175 ++error;
2178 2176 }
2179 2177 goto out;
2180 2178 }
2181 2179 } else {
2182 2180 if ((targattrfd = openat(targetdirfd, dp->d_name,
2183 2181 O_RDONLY)) == -1) {
2184 2182 if (!attrsilent) {
2185 2183 (void) fprintf(stderr,
2186 2184 gettext("%s: could not open attribute"
2187 2185 " %s on file %s: "), cmd, dp->d_name,
2188 2186 target);
2189 2187 perror("");
2190 2188 ++error;
2191 2189 }
2192 2190 goto out;
2193 2191 }
2194 2192 }
2195 2193
2196 2194
2197 2195 if (fstat(targattrfd, &s4) < 0) {
2198 2196 if (!attrsilent) {
2199 2197 (void) fprintf(stderr,
2200 2198 gettext("%s: could not stat attribute"
2201 2199 " %s on file"
2202 2200 " %s: "), cmd, dp->d_name, target);
2203 2201 perror("");
2204 2202 ++error;
2205 2203 }
2206 2204 }
2207 2205
2208 2206 out:
2209 2207 if (error) {
2210 2208 if (srcattrfd != -1)
2211 2209 (void) close(srcattrfd);
2212 2210 if (targattrfd != -1)
2213 2211 (void) close(targattrfd);
2214 2212 srcattrfd = targattrfd = -1;
2215 2213 }
2216 2214 return (error == 0 ? 0 :1);
2217 2215 }
2218 2216
2219 2217 void
2220 2218 rewind_attrdir(DIR * sdp)
2221 2219 {
2222 2220 int pwdfd;
2223 2221
2224 2222 pwdfd = open(".", O_RDONLY);
2225 2223 if ((pwdfd != -1) && (fchdir(sourcedirfd) == 0)) {
2226 2224 rewinddir(sdp);
2227 2225 (void) fchdir(pwdfd);
2228 2226 (void) close(pwdfd);
2229 2227 } else {
2230 2228 if (!attrsilent) {
2231 2229 (void) fprintf(stderr, gettext("%s: "
2232 2230 "failed to rewind attribute dir\n"),
2233 2231 cmd);
2234 2232 }
2235 2233 }
2236 2234 }
2237 2235
2238 2236 void
2239 2237 close_all()
2240 2238 {
2241 2239 if (srcattrfd != -1)
2242 2240 (void) close(srcattrfd);
2243 2241 if (targattrfd != -1)
2244 2242 (void) close(targattrfd);
2245 2243 if (sourcedirfd != -1)
2246 2244 (void) close(sourcedirfd);
2247 2245 if (targetdirfd != -1)
2248 2246 (void) close(targetdirfd);
2249 2247 if (srcdirp != NULL) {
2250 2248 (void) closedir(srcdirp);
2251 2249 srcdirp = NULL;
2252 2250 }
2253 2251 if (srcfd != -1)
2254 2252 (void) close(srcfd);
2255 2253 if (targfd != -1)
2256 2254 (void) close(targfd);
2257 2255 }
↓ open down ↓ |
271 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX