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