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