Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/lib/libcmd/cp.c
1808 views
1
/***********************************************************************
2
* *
3
* This software is part of the ast package *
4
* Copyright (c) 1992-2012 AT&T Intellectual Property *
5
* and is licensed under the *
6
* Eclipse Public License, Version 1.0 *
7
* by AT&T Intellectual Property *
8
* *
9
* A copy of the License is available at *
10
* http://www.eclipse.org/org/documents/epl-v10.html *
11
* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
12
* *
13
* Information and Software Systems Research *
14
* AT&T Research *
15
* Florham Park NJ *
16
* *
17
* Glenn Fowler <[email protected]> *
18
* David Korn <[email protected]> *
19
* *
20
***********************************************************************/
21
#pragma prototyped
22
/*
23
* Glenn Fowler
24
* AT&T Research
25
*
26
* cp/ln/mv -- copy/link/move files
27
*/
28
29
static const char usage_head[] =
30
"[-?@(#)$Id: cp (AT&T Research) 2012-04-20 $\n]"
31
USAGE_LICENSE
32
;
33
34
static const char usage_cp[] =
35
"[+NAME?cp - copy files]"
36
"[+DESCRIPTION?If the last argument names an existing directory, \bcp\b "
37
"copies each \afile\a into a file with the same name in that directory. "
38
"Otherwise, if only two files are given, \bcp\b copies the first onto "
39
"the second. It is an error if the last argument is not a directory and "
40
"more than two files are given. By default directories are not copied.]"
41
42
"[a:archive?Preserve as much as possible of the structure and attributes "
43
"of the original files in the copy. Equivalent to \b--physical\b "
44
"\b--preserve\b \b--recursive\b.]"
45
"[A:attributes?Preserve selected file attributes:]:[eipt]"
46
"{"
47
"[+e?Everything permissible.]"
48
"[+i?Owner uid and gid.]"
49
"[+p?Permissions.]"
50
"[+t?Access and modify times.]"
51
"}"
52
"[p:preserve?Preserve file owner, group, permissions and timestamps.]"
53
"[h:hierarchy|parents?Form the name of each destination file by "
54
"appending to the target directory a slash and the specified source file "
55
"name. The last argument must be an existing directory. Missing "
56
"destination directories are created.]"
57
"[H:metaphysical?Follow command argument symbolic links, otherwise don't "
58
"follow.]"
59
"[l:link?Make hard links to destination files instead of copies.]"
60
"[U:remove-destination?Remove existing destination files before copying.]"
61
"[L:logical|dereference?Follow symbolic links and copy the files they "
62
"point to.]"
63
"[P|d:physical|nodereference?Don't follow symbolic links; copy symbolic "
64
"rather than the files they point to.]"
65
;
66
67
static const char usage_ln[] =
68
"[+NAME?ln - link files]"
69
"[+DESCRIPTION?If the last argument names an existing directory, \bln\b "
70
"links each \afile\a into a file with the same name in that directory. "
71
"Otherwise, if only two files are given, \bln\b links the first onto the "
72
"second. It is an error if the last argument is not a directory and more "
73
"than two files are given. By default directories are not linked.]"
74
;
75
76
static const char usage_mv[] =
77
"[+NAME?mv - rename files]"
78
"[+DESCRIPTION?If the last argument names an existing directory, \bmv\b "
79
"renames each \afile\a into a file with the same name in that directory. "
80
"Otherwise, if only two files are given, \bmv\b renames the first onto "
81
"the second. It is an error if the last argument is not a directory and "
82
"more than two files are given. If a source and destination file reside "
83
"on different filesystems then \bmv\b copies the file contents to the "
84
"destination and then deletes the source file.]"
85
86
"[U:remove-destination?Remove existing destination files before moving.]"
87
;
88
89
static const char usage_tail[] =
90
"[f:force?Replace existing destination files.]"
91
"[i:interactive|prompt?Prompt whether to replace existing destination "
92
"files. An affirmative response (\by\b or \bY\b) replaces the file, a "
93
"quit response (\bq\b or \bQ\b) exits immediately, and all other "
94
"responses skip the file.]"
95
"[r|R:recursive?Operate on the contents of directories recursively.]"
96
"[s:symlink|symbolic-link?Make symbolic links to destination files.]"
97
"[u:update?Replace a destination file only if its modification time is "
98
"older than the corresponding source file modification time.]"
99
"[v:verbose?Print the name of each file before operating on it.]"
100
"[F:fsync|sync?\bfsync\b(2) each file after it is copied.]"
101
"[B:backup?Make backups of files that are about to be replaced. "
102
"\b--suffix\b sets the backup suffix. The backup type is determined in "
103
"this order: this option, the \bVERSION_CONTROL\b environment variable, "
104
"or the default value \bexisting\b. \atype\a may be one of:]:?[type]"
105
"{"
106
"[+numbered|t?Always make numbered backups. The numbered backup "
107
"suffix is \b.\aSNS\a, where \aS\a is the \bbackup-suffix\b and "
108
"\aN\a is the version number, starting at 1, incremented with "
109
"each version.]"
110
"[+existing|nil?Make numbered backups of files that already have "
111
"them, otherwise simple backups.]"
112
"[+simple|never?Always make simple backups.]"
113
"[+none|off?Disable backups.]"
114
"}"
115
"[S:suffix?A backup file is made by renaming the file to the same name "
116
"with the backup suffix appended. The backup suffix is determined in "
117
"this order: this option, the \bSIMPLE_BACKUP_SUFFIX\b, environment "
118
"variable, or the default value \b~\b.]:[suffix]"
119
"[b?\b--backup\b using the type in the \bVERSION_CONTROL\b environment "
120
"variable.]"
121
"[x|X:xdev|local|mount|one-file-system?Do not descend into directories "
122
"in different filesystems than their parents.]"
123
124
"\n"
125
"\nsource destination\n"
126
"file ... directory\n"
127
"\n"
128
129
"[+SEE ALSO?\bpax\b(1), \bfsync\b(2), \brename\b(2), \bunlink\b(2),"
130
" \bremove\b(3)]"
131
;
132
133
#include <cmd.h>
134
#include <ls.h>
135
#include <times.h>
136
#include <fts_fix.h>
137
#include <fs3d.h>
138
#include <hashkey.h>
139
#include <stk.h>
140
#include <tmx.h>
141
142
#define PATH_CHUNK 256
143
144
#define CP 1
145
#define LN 2
146
#define MV 3
147
148
#define PRESERVE_IDS 0x1 /* preserve uid gid */
149
#define PRESERVE_PERM 0x2 /* preserve permissions */
150
#define PRESERVE_TIME 0x4 /* preserve times */
151
152
#define BAK_replace 0 /* no backup -- just replace */
153
#define BAK_existing 1 /* number if already else simple*/
154
#define BAK_number 2 /* append .suffix number suffix */
155
#define BAK_simple 3 /* append suffix */
156
157
typedef struct State_s /* program state */
158
{
159
Shbltin_t* context; /* builtin context */
160
int backup; /* BAK_* type */
161
int directory; /* destination is directory */
162
int flags; /* FTS_* flags */
163
int force; /* force approval */
164
int fs3d; /* 3d fs enabled */
165
int hierarchy; /* preserve hierarchy */
166
int interactive; /* prompt for approval */
167
int missmode; /* default missing dir mode */
168
int official; /* move to next view */
169
int op; /* {CP,LN,MV} */
170
int perm; /* permissions to preserve */
171
int postsiz; /* state.path post index */
172
int presiz; /* state.path pre index */
173
int preserve; /* preserve { ids perms times } */
174
int recursive; /* subtrees too */
175
int remove; /* remove destination before op */
176
int suflen; /* strlen(state.suffix) */
177
int sync; /* fsync() each file after copy */
178
int uid; /* caller uid */
179
int update; /* replace only if newer */
180
int verbose; /* list each file before op */
181
int wflags; /* open() for write flags */
182
183
int (*link)(const char*, const char*); /* link */
184
int (*stat)(const char*, struct stat*); /* stat */
185
186
#define INITSTATE pathsiz /* (re)init state before this */
187
int pathsiz; /* state.path buffer size */
188
189
190
char* path; /* to pathname buffer */
191
char* opname; /* state.op message string */
192
char* suffix; /* backup suffix */
193
194
Sfio_t* tmp; /* tmp string stream */
195
196
char text[PATH_MAX]; /* link text buffer */
197
} State_t;
198
199
static const char dot[2] = { '.' };
200
201
/*
202
* preserve support
203
*/
204
205
static void
206
preserve(State_t* state, const char* path, struct stat* ns, struct stat* os)
207
{
208
int n;
209
210
if ((state->preserve & PRESERVE_TIME) && tmxtouch(path, tmxgetatime(os), tmxgetmtime(os), TMX_NOTIME, 0))
211
error(ERROR_SYSTEM|2, "%s: cannot reset access and modify times", path);
212
if (state->preserve & PRESERVE_IDS)
213
{
214
n = ((ns->st_uid != os->st_uid) << 1) | (ns->st_gid != os->st_gid);
215
if (n && chown(state->path, os->st_uid, os->st_gid))
216
switch (n)
217
{
218
case 01:
219
error(ERROR_SYSTEM|2, "%s: cannot reset group to %s", path, fmtgid(os->st_gid));
220
break;
221
case 02:
222
error(ERROR_SYSTEM|2, "%s: cannot reset owner to %s", path, fmtuid(os->st_uid));
223
break;
224
case 03:
225
error(ERROR_SYSTEM|2, "%s: cannot reset owner to %s and group to %s", path, fmtuid(os->st_uid), fmtgid(os->st_gid));
226
break;
227
}
228
}
229
}
230
231
/*
232
* visit a single file and state.op to the destination
233
*/
234
235
static int
236
visit(State_t* state, register FTSENT* ent)
237
{
238
register char* base;
239
register int n;
240
register int len;
241
int rm;
242
int rfd;
243
int wfd;
244
int m;
245
int v;
246
char* s;
247
char* e;
248
char* protection;
249
Sfio_t* ip;
250
Sfio_t* op;
251
FTS* fts;
252
FTSENT* sub;
253
struct stat st;
254
255
if (ent->fts_info == FTS_DC)
256
{
257
error(2, "%s: directory causes cycle", ent->fts_path);
258
fts_set(NiL, ent, FTS_SKIP);
259
return 0;
260
}
261
if (ent->fts_level == 0)
262
{
263
base = ent->fts_name;
264
len = ent->fts_namelen;
265
if (state->hierarchy)
266
state->presiz = -1;
267
else
268
{
269
state->presiz = ent->fts_pathlen;
270
while (*base == '.' && *(base + 1) == '/')
271
for (base += 2; *base == '/'; base++);
272
if (*base == '.' && !*(base + 1))
273
state->presiz--;
274
else if (*base)
275
state->presiz -= base - ent->fts_name;
276
base = ent->fts_name + len;
277
while (base > ent->fts_name && *(base - 1) == '/')
278
base--;
279
while (base > ent->fts_name && *(base - 1) != '/')
280
base--;
281
len -= base - ent->fts_name;
282
if (state->directory)
283
state->presiz -= len + 1;
284
}
285
}
286
else
287
{
288
base = ent->fts_path + state->presiz + 1;
289
len = ent->fts_pathlen - state->presiz - 1;
290
}
291
len++;
292
if (state->directory)
293
{
294
if ((state->postsiz + len) > state->pathsiz && !(state->path = newof(state->path, char, state->pathsiz = roundof(state->postsiz + len, PATH_CHUNK), 0)))
295
error(ERROR_SYSTEM|3, "out of space");
296
if (state->hierarchy && ent->fts_level == 0 && strchr(base, '/'))
297
{
298
s = state->path + state->postsiz;
299
memcpy(s, base, len);
300
while (e = strchr(s, '/'))
301
{
302
*e = 0;
303
if (access(state->path, F_OK))
304
{
305
st.st_mode = state->missmode;
306
if (s = strrchr(s, '/'))
307
{
308
*s = 0;
309
stat(state->path, &st);
310
*s = '/';
311
}
312
if (mkdir(state->path, st.st_mode & S_IPERM))
313
{
314
error(ERROR_SYSTEM|2, "%s: cannot create directory -- %s ignored", state->path, ent->fts_path);
315
fts_set(NiL, ent, FTS_SKIP);
316
return 0;
317
}
318
}
319
*e++ = '/';
320
s = e;
321
}
322
}
323
}
324
switch (ent->fts_info)
325
{
326
case FTS_DP:
327
if (state->preserve && state->op != LN || ent->fts_level > 0 && (ent->fts_statp->st_mode & S_IRWXU) != S_IRWXU)
328
{
329
if (len && ent->fts_level > 0)
330
memcpy(state->path + state->postsiz, base, len);
331
else
332
state->path[state->postsiz] = 0;
333
if (stat(state->path, &st))
334
error(ERROR_SYSTEM|2, "%s: cannot stat", state->path);
335
else
336
{
337
if ((ent->fts_statp->st_mode & S_IPERM) != (st.st_mode & S_IPERM) && chmod(state->path, ent->fts_statp->st_mode & S_IPERM))
338
error(ERROR_SYSTEM|2, "%s: cannot reset directory mode to %s", state->path, fmtmode(st.st_mode & S_IPERM, 0) + 1);
339
if (state->preserve & (PRESERVE_IDS|PRESERVE_TIME))
340
preserve(state, state->path, &st, ent->fts_statp);
341
}
342
}
343
return 0;
344
case FTS_DNR:
345
case FTS_DNX:
346
case FTS_D:
347
if (!state->recursive)
348
{
349
fts_set(NiL, ent, FTS_SKIP);
350
if (state->op == CP)
351
error(1, "%s: directory -- copying as plain file", ent->fts_path);
352
else if (state->link == link && !state->force)
353
{
354
error(2, "%s: cannot link directory", ent->fts_path);
355
return 0;
356
}
357
}
358
else switch (ent->fts_info)
359
{
360
case FTS_DNR:
361
error(2, "%s: cannot read directory", ent->fts_path);
362
return 0;
363
case FTS_DNX:
364
error(2, "%s: cannot search directory", ent->fts_path);
365
fts_set(NiL, ent, FTS_SKIP);
366
367
/*FALLTHROUGH*/
368
case FTS_D:
369
if (state->directory)
370
memcpy(state->path + state->postsiz, base, len);
371
if (!(*state->stat)(state->path, &st))
372
{
373
if (!S_ISDIR(st.st_mode))
374
{
375
error(2, "%s: not a directory -- %s ignored", state->path, ent->fts_path);
376
return 0;
377
}
378
}
379
else if (mkdir(state->path, (ent->fts_statp->st_mode & S_IPERM)|(ent->fts_info == FTS_D ? S_IRWXU : 0)))
380
{
381
error(ERROR_SYSTEM|2, "%s: cannot create directory -- %s ignored", state->path, ent->fts_path);
382
fts_set(NiL, ent, FTS_SKIP);
383
}
384
if (!state->directory)
385
{
386
state->directory = 1;
387
state->path[state->postsiz++] = '/';
388
state->presiz--;
389
}
390
return 0;
391
}
392
break;
393
case FTS_ERR:
394
case FTS_NS:
395
case FTS_SLNONE:
396
if (state->link != pathsetlink)
397
{
398
error(2, "%s: not found", ent->fts_path);
399
return 0;
400
}
401
break;
402
#if 0
403
case FTS_SL:
404
if (state->op == CP)
405
{
406
error(2, "%s: cannot copy non-terminal symbolic link", ent->fts_path);
407
return 0;
408
}
409
break;
410
#endif
411
}
412
if (state->directory)
413
memcpy(state->path + state->postsiz, base, len);
414
if ((*state->stat)(state->path, &st))
415
st.st_mode = 0;
416
else if (state->update && !S_ISDIR(st.st_mode) && (unsigned long)ent->fts_statp->st_mtime < (unsigned long)st.st_mtime)
417
{
418
fts_set(NiL, ent, FTS_SKIP);
419
return 0;
420
}
421
else if (!state->fs3d || !iview(&st))
422
{
423
/*
424
* target is in top 3d view
425
*/
426
427
if (state->op != LN && st.st_dev == ent->fts_statp->st_dev && st.st_ino == ent->fts_statp->st_ino)
428
{
429
if (state->op == MV)
430
{
431
/*
432
* let rename() handle it
433
*/
434
435
if (state->verbose)
436
sfputr(sfstdout, state->path, '\n');
437
goto operate;
438
}
439
if (!state->official)
440
error(2, "%s: identical to %s", state->path, ent->fts_path);
441
return 0;
442
}
443
if (S_ISDIR(st.st_mode))
444
{
445
error(2, "%s: cannot %s existing directory", state->path, state->opname);
446
return 0;
447
}
448
if (state->verbose)
449
sfputr(sfstdout, state->path, '\n');
450
rm = state->remove || ent->fts_info == FTS_SL;
451
if (!rm || !state->force)
452
{
453
if (S_ISLNK(st.st_mode) && (n = -1) || (n = open(state->path, O_RDWR|O_BINARY|O_cloexec)) >= 0)
454
{
455
if (n >= 0)
456
close(n);
457
if (state->force)
458
/* ok */;
459
else if (state->interactive)
460
{
461
if ((n = astquery(-1, "%s %s? ", state->opname, state->path)) < 0 || sh_checksig(state->context))
462
return -1;
463
if (n)
464
return 0;
465
}
466
else if (state->op == LN)
467
{
468
error(2, "%s: cannot %s existing file", state->path, state->opname);
469
return 0;
470
}
471
}
472
else if (state->force)
473
rm = 1;
474
else
475
{
476
protection =
477
#ifdef ETXTBSY
478
errno == ETXTBSY ? "``running program''" :
479
#endif
480
st.st_uid != state->uid ? "``not owner''" :
481
fmtmode(st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO), 0) + 1;
482
if (state->interactive)
483
{
484
if ((n = astquery(-1, "override protection %s for %s? ", protection, state->path)) < 0 || sh_checksig(state->context))
485
return -1;
486
if (n)
487
return 0;
488
rm = 1;
489
}
490
else if (!rm)
491
{
492
error(2, "%s: cannot %s %s protection", state->path, state->opname, protection);
493
return 0;
494
}
495
}
496
}
497
switch (state->backup)
498
{
499
case BAK_existing:
500
case BAK_number:
501
v = 0;
502
if (s = strrchr(state->path, '/'))
503
{
504
e = state->path;
505
*s++ = 0;
506
}
507
else
508
{
509
e = (char*)dot;
510
s = state->path;
511
}
512
n = strlen(s);
513
if (fts = fts_open((char**)e, FTS_NOCHDIR|FTS_ONEPATH|FTS_PHYSICAL|FTS_NOPOSTORDER|FTS_NOSTAT|FTS_NOSEEDOTDIR, NiL))
514
{
515
while (sub = fts_read(fts))
516
{
517
if (strneq(s, sub->fts_name, n) && sub->fts_name[n] == '.' && strneq(sub->fts_name + n + 1, state->suffix, state->suflen) && (m = strtol(sub->fts_name + n + state->suflen + 1, &e, 10)) && streq(e, state->suffix) && m > v)
518
v = m;
519
if (sub->fts_level)
520
fts_set(NiL, sub, FTS_SKIP);
521
}
522
fts_close(fts);
523
}
524
if (s != state->path)
525
*--s = '/';
526
if (v || state->backup == BAK_number)
527
{
528
sfprintf(state->tmp, "%s.%s%d%s", state->path, state->suffix, v + 1, state->suffix);
529
goto backup;
530
}
531
/*FALLTHROUGH*/
532
case BAK_simple:
533
sfprintf(state->tmp, "%s%s", state->path, state->suffix);
534
backup:
535
if (!(s = sfstruse(state->tmp)))
536
error(ERROR_SYSTEM|3, "%s: out of space", state->path);
537
if (rename(state->path, s))
538
{
539
error(ERROR_SYSTEM|2, "%s: cannot backup to %s", state->path, s);
540
return 0;
541
}
542
break;
543
default:
544
if (rm && remove(state->path))
545
{
546
error(ERROR_SYSTEM|2, "%s: cannot remove", state->path);
547
return 0;
548
}
549
break;
550
}
551
}
552
operate:
553
switch (state->op)
554
{
555
case MV:
556
for (;;)
557
{
558
if (!rename(ent->fts_path, state->path))
559
return 0;
560
if (errno == ENOENT)
561
rm = 1;
562
else if (!rm && st.st_mode && !remove(state->path))
563
{
564
rm = 1;
565
continue;
566
}
567
if (errno != EXDEV && (rm || S_ISDIR(ent->fts_statp->st_mode)))
568
{
569
error(ERROR_SYSTEM|2, "%s: cannot rename to %s", ent->fts_path, state->path);
570
return 0;
571
}
572
else
573
break;
574
}
575
/*FALLTHROUGH*/
576
case CP:
577
if (S_ISLNK(ent->fts_statp->st_mode))
578
{
579
if ((n = pathgetlink(ent->fts_path, state->text, sizeof(state->text) - 1)) < 0)
580
{
581
error(ERROR_SYSTEM|2, "%s: cannot read symbolic link text", ent->fts_path);
582
return 0;
583
}
584
state->text[n] = 0;
585
if (pathsetlink(state->text, state->path))
586
{
587
error(ERROR_SYSTEM|2, "%s: cannot copy symbolic link to %s", ent->fts_path, state->path);
588
return 0;
589
}
590
}
591
else if (state->op == CP || S_ISREG(ent->fts_statp->st_mode) || S_ISDIR(ent->fts_statp->st_mode))
592
{
593
if (ent->fts_statp->st_size > 0 && (rfd = open(ent->fts_path, O_RDONLY|O_BINARY|O_cloexec)) < 0)
594
{
595
error(ERROR_SYSTEM|2, "%s: cannot read", ent->fts_path);
596
return 0;
597
}
598
else if ((wfd = open(state->path, (st.st_mode ? (state->wflags & ~O_EXCL) : state->wflags)|O_cloexec, ent->fts_statp->st_mode & state->perm)) < 0)
599
{
600
error(ERROR_SYSTEM|2, "%s: cannot write", state->path);
601
if (ent->fts_statp->st_size > 0)
602
close(rfd);
603
return 0;
604
}
605
else if (ent->fts_statp->st_size > 0)
606
{
607
if (!(ip = sfnew(NiL, NiL, SF_UNBOUND, rfd, SF_READ)))
608
{
609
error(ERROR_SYSTEM|2, "%s: %s read stream error", ent->fts_path, state->path);
610
close(rfd);
611
close(wfd);
612
return 0;
613
}
614
if (!(op = sfnew(NiL, NiL, SF_UNBOUND, wfd, SF_WRITE)))
615
{
616
error(ERROR_SYSTEM|2, "%s: %s write stream error", ent->fts_path, state->path);
617
close(wfd);
618
sfclose(ip);
619
return 0;
620
}
621
n = 0;
622
if (sfmove(ip, op, (Sfoff_t)SF_UNBOUND, -1) < 0)
623
n |= 3;
624
if (!sfeof(ip))
625
n |= 1;
626
if (sfsync(op) || state->sync && fsync(wfd) || sfclose(op))
627
n |= 2;
628
if (sfclose(ip))
629
n |= 1;
630
if (n)
631
{
632
error(ERROR_SYSTEM|2, "%s: %s %s error", ent->fts_path, state->path, n == 1 ? ERROR_translate(0, 0, 0, "read") : n == 2 ? ERROR_translate(0, 0, 0, "write") : ERROR_translate(0, 0, 0, "io"));
633
return 0;
634
}
635
}
636
else
637
close(wfd);
638
}
639
else if (S_ISBLK(ent->fts_statp->st_mode) || S_ISCHR(ent->fts_statp->st_mode) || S_ISFIFO(ent->fts_statp->st_mode))
640
{
641
if (mknod(state->path, ent->fts_statp->st_mode, idevice(ent->fts_statp)))
642
{
643
error(ERROR_SYSTEM|2, "%s: cannot copy special file to %s", ent->fts_path, state->path);
644
return 0;
645
}
646
}
647
else
648
{
649
error(2, "%s: cannot copy -- unknown file type 0%o", ent->fts_path, S_ITYPE(ent->fts_statp->st_mode));
650
return 0;
651
}
652
if (state->preserve)
653
{
654
if (ent->fts_info != FTS_SL)
655
{
656
if (stat(state->path, &st))
657
error(ERROR_SYSTEM|2, "%s: cannot stat", state->path);
658
else
659
{
660
if ((state->preserve & PRESERVE_PERM) && (ent->fts_statp->st_mode & state->perm) != (st.st_mode & state->perm) && chmod(state->path, ent->fts_statp->st_mode & state->perm))
661
error(ERROR_SYSTEM|2, "%s: cannot reset mode to %s", state->path, fmtmode(st.st_mode & state->perm, 0) + 1);
662
if (state->preserve & (PRESERVE_IDS|PRESERVE_TIME))
663
preserve(state, state->path, &st, ent->fts_statp);
664
}
665
}
666
if (state->op == MV && remove(ent->fts_path))
667
error(ERROR_SYSTEM|1, "%s: cannot remove", ent->fts_path);
668
}
669
break;
670
case LN:
671
if ((*state->link)(ent->fts_path, state->path))
672
error(ERROR_SYSTEM|2, "%s: cannot link to %s", ent->fts_path, state->path);
673
break;
674
}
675
return 0;
676
}
677
678
int
679
b_cp(int argc, register char** argv, Shbltin_t* context)
680
{
681
register char* file;
682
register char* s;
683
char** v;
684
char* backup_type;
685
FTS* fts;
686
FTSENT* ent;
687
const char* usage;
688
int path_resolve;
689
int standard;
690
struct stat st;
691
State_t* state;
692
Shbltin_t* sh;
693
Shbltin_t* cleanup = context;
694
695
cmdinit(argc, argv, context, ERROR_CATALOG, ERROR_NOTIFY);
696
if (!(sh = CMD_CONTEXT(context)) || !(state = (State_t*)sh->ptr))
697
{
698
if (!(state = newof(0, State_t, 1, 0)))
699
error(ERROR_SYSTEM|3, "out of space");
700
if (sh)
701
sh->ptr = state;
702
}
703
else
704
memset(state, 0, offsetof(State_t, INITSTATE));
705
state->context = context;
706
state->presiz = -1;
707
backup_type = 0;
708
state->flags = FTS_NOCHDIR|FTS_NOSEEDOTDIR;
709
state->uid = geteuid();
710
state->wflags = O_WRONLY|O_CREAT|O_TRUNC|O_BINARY;
711
if (!state->tmp && !(state->tmp = sfstropen()))
712
error(ERROR_SYSTEM|3, "out of space [tmp string]");
713
sfputr(state->tmp, usage_head, -1);
714
standard = !!conformance(0, 0);
715
switch (error_info.id[0])
716
{
717
case 'c':
718
case 'C':
719
sfputr(state->tmp, usage_cp, -1);
720
state->op = CP;
721
state->stat = stat;
722
path_resolve = -1;
723
break;
724
case 'l':
725
case 'L':
726
sfputr(state->tmp, usage_ln, -1);
727
state->op = LN;
728
state->flags |= FTS_PHYSICAL;
729
state->link = link;
730
state->remove = 1;
731
state->stat = lstat;
732
path_resolve = 1;
733
break;
734
case 'm':
735
case 'M':
736
sfputr(state->tmp, usage_mv, -1);
737
state->op = MV;
738
state->flags |= FTS_PHYSICAL;
739
state->preserve = PRESERVE_IDS|PRESERVE_PERM|PRESERVE_TIME;
740
state->stat = lstat;
741
path_resolve = 1;
742
break;
743
default:
744
error(3, "not implemented");
745
break;
746
}
747
sfputr(state->tmp, usage_tail, -1);
748
if (!(usage = sfstruse(state->tmp)))
749
error(ERROR_SYSTEM|3, "%s: out of space", state->path);
750
state->opname = state->op == CP ? ERROR_translate(0, 0, 0, "overwrite") : ERROR_translate(0, 0, 0, "replace");
751
for (;;)
752
{
753
switch (optget(argv, usage))
754
{
755
case 'a':
756
state->flags |= FTS_PHYSICAL;
757
state->preserve = PRESERVE_IDS|PRESERVE_PERM|PRESERVE_TIME;
758
state->recursive = 1;
759
path_resolve = 1;
760
continue;
761
case 'A':
762
s = opt_info.arg;
763
for (;;)
764
{
765
switch (*s++)
766
{
767
case 0:
768
break;
769
case 'e':
770
state->preserve |= PRESERVE_IDS|PRESERVE_PERM|PRESERVE_TIME;
771
continue;
772
case 'i':
773
state->preserve |= PRESERVE_IDS;
774
continue;
775
case 'p':
776
state->preserve |= PRESERVE_PERM;
777
continue;
778
case 't':
779
state->preserve |= PRESERVE_TIME;
780
continue;
781
default:
782
error(1, "%s=%c: unknown attribute flag", opt_info.option, *(s - 1));
783
continue;
784
}
785
break;
786
}
787
continue;
788
case 'b':
789
state->backup = 1;
790
continue;
791
case 'f':
792
state->force = 1;
793
if (state->op != CP || !standard)
794
state->interactive = 0;
795
continue;
796
case 'h':
797
state->hierarchy = 1;
798
continue;
799
case 'i':
800
state->interactive = 1;
801
if (state->op != CP || !standard)
802
state->force = 0;
803
continue;
804
case 'l':
805
state->op = LN;
806
state->link = link;
807
state->stat = lstat;
808
continue;
809
case 'p':
810
state->preserve = PRESERVE_IDS|PRESERVE_PERM|PRESERVE_TIME;
811
continue;
812
case 'r':
813
state->recursive = 1;
814
if (path_resolve < 0)
815
path_resolve = 0;
816
continue;
817
case 's':
818
state->op = LN;
819
state->link = pathsetlink;
820
state->stat = lstat;
821
continue;
822
case 'u':
823
state->update = 1;
824
continue;
825
case 'v':
826
state->verbose = 1;
827
continue;
828
case 'x':
829
state->flags |= FTS_XDEV;
830
continue;
831
case 'B':
832
backup_type = opt_info.arg;
833
state->backup = 1;
834
continue;
835
case 'F':
836
#if _lib_fsync
837
state->sync = 1;
838
#else
839
error(1, "%s not implemented on this system", opt_info.name);
840
#endif
841
continue;
842
case 'H':
843
state->flags |= FTS_META|FTS_PHYSICAL;
844
path_resolve = 1;
845
continue;
846
case 'L':
847
state->flags &= ~FTS_PHYSICAL;
848
path_resolve = 1;
849
continue;
850
case 'P':
851
state->flags &= ~FTS_META;
852
state->flags |= FTS_PHYSICAL;
853
path_resolve = 1;
854
continue;
855
case 'R':
856
state->recursive = 1;
857
state->flags &= ~FTS_META;
858
state->flags |= FTS_PHYSICAL;
859
path_resolve = 1;
860
continue;
861
case 'S':
862
state->suffix = opt_info.arg;
863
continue;
864
case 'U':
865
state->remove = 1;
866
continue;
867
case '?':
868
error(ERROR_USAGE|4, "%s", opt_info.arg);
869
continue;
870
case ':':
871
error(2, "%s", opt_info.arg);
872
continue;
873
}
874
break;
875
}
876
argc -= opt_info.index + 1;
877
argv += opt_info.index;
878
if (*argv && streq(*argv, "-") && !streq(*(argv - 1), "--"))
879
{
880
argc--;
881
argv++;
882
}
883
if (!(v = (char**)stkalloc(stkstd, (argc + 2) * sizeof(char*))))
884
error(ERROR_SYSTEM|3, "out of space");
885
memcpy(v, argv, (argc + 1) * sizeof(char*));
886
argv = v;
887
if (!standard)
888
{
889
state->wflags |= O_EXCL;
890
if (!argc)
891
{
892
argc++;
893
argv[1] = (char*)dot;
894
}
895
}
896
if (state->backup)
897
{
898
if (!(file = backup_type) && !(backup_type = getenv("VERSION_CONTROL")))
899
state->backup = 0;
900
else
901
switch (strkey(backup_type))
902
{
903
case HASHKEY6('e','x','i','s','t','i'):
904
case HASHKEY5('e','x','i','s','t'):
905
case HASHKEY4('e','x','i','s'):
906
case HASHKEY3('e','x','i'):
907
case HASHKEY2('e','x'):
908
case HASHKEY1('e'):
909
case HASHKEY3('n','i','l'):
910
case HASHKEY2('n','i'):
911
state->backup = BAK_existing;
912
break;
913
case HASHKEY5('n','e','v','e','r'):
914
case HASHKEY4('n','e','v','e'):
915
case HASHKEY3('n','e','v'):
916
case HASHKEY2('n','e'):
917
case HASHKEY6('s','i','m','p','l','e'):
918
case HASHKEY5('s','i','m','p','l'):
919
case HASHKEY4('s','i','m','p'):
920
case HASHKEY3('s','i','m'):
921
case HASHKEY2('s','i'):
922
case HASHKEY1('s'):
923
state->backup = BAK_simple;
924
break;
925
case HASHKEY4('n','o','n','e'):
926
case HASHKEY3('n','o','n'):
927
case HASHKEY2('n','o'):
928
case HASHKEY3('o','f','f'):
929
case HASHKEY2('o','f'):
930
case HASHKEY1('o'):
931
state->backup = 0;
932
break;
933
case HASHKEY6('n','u','m','b','e','r'):
934
case HASHKEY5('n','u','m','b','e'):
935
case HASHKEY4('n','u','m','b'):
936
case HASHKEY3('n','u','m'):
937
case HASHKEY2('n','u'):
938
case HASHKEY1('t'):
939
state->backup = BAK_number;
940
break;
941
default:
942
if (file)
943
error(2, "%s: unknown backup type", backup_type);
944
break;
945
}
946
if (!state->suffix && !(state->suffix = getenv("SIMPLE_BACKUP_SUFFIX")))
947
state->suffix = "~";
948
state->suflen = strlen(state->suffix);
949
}
950
if (argc <= 0 || error_info.errors)
951
error(ERROR_USAGE|4, "%s", optusage(NiL));
952
if (!path_resolve)
953
state->flags |= fts_flags() | FTS_META;
954
file = argv[argc];
955
argv[argc] = 0;
956
if (s = strrchr(file, '/'))
957
{
958
while (*s == '/')
959
s++;
960
if (!(!*s || *s == '.' && (!*++s || *s == '.' && !*++s)))
961
s = 0;
962
}
963
if (file != (char*)dot)
964
pathcanon(file, 0, 0);
965
if (!(state->directory = !stat(file, &st) && S_ISDIR(st.st_mode)) && argc > 1)
966
error(ERROR_USAGE|4, "%s", optusage(NiL));
967
if (s && !state->directory)
968
error(3, "%s: not a directory", file);
969
if ((state->fs3d = fs3d(FS3D_TEST)) && strmatch(file, "...|*/...|.../*"))
970
state->official = 1;
971
state->postsiz = strlen(file);
972
if (state->pathsiz < roundof(state->postsiz + 2, PATH_CHUNK) && !(state->path = newof(state->path, char, state->pathsiz = roundof(state->postsiz + 2, PATH_CHUNK), 0)))
973
error(ERROR_SYSTEM|3, "out of space");
974
memcpy(state->path, file, state->postsiz + 1);
975
if (state->directory && state->path[state->postsiz - 1] != '/')
976
state->path[state->postsiz++] = '/';
977
if (state->hierarchy)
978
{
979
if (!state->directory)
980
error(3, "%s: last argument must be a directory", file);
981
state->missmode = st.st_mode;
982
}
983
state->perm = state->uid ? S_IPERM : (S_IPERM & ~S_ISVTX);
984
if (!state->recursive)
985
state->flags |= FTS_TOP;
986
if (fts = fts_open(argv, state->flags, NiL))
987
{
988
while (!sh_checksig(context) && (ent = fts_read(fts)) && !visit(state, ent));
989
fts_close(fts);
990
}
991
else if (state->link != pathsetlink)
992
switch (state->op)
993
{
994
case CP:
995
error(ERROR_SYSTEM|2, "%s: cannot copy", argv[0]);
996
break;
997
case LN:
998
error(ERROR_SYSTEM|2, "%s: cannot link", argv[0]);
999
break;
1000
case MV:
1001
error(ERROR_SYSTEM|2, "%s: cannot move", argv[0]);
1002
break;
1003
}
1004
else if ((*state->link)(*argv, state->path))
1005
error(ERROR_SYSTEM|2, "%s: cannot link to %s", *argv, state->path);
1006
if (cleanup && !sh)
1007
{
1008
if (state->path)
1009
free(state->path);
1010
free(state);
1011
}
1012
return error_info.errors != 0;
1013
}
1014
1015