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