|
|
1.1 root 1:
2: /*
3: * cp source dest
4: *
5: * or
6: *
7: * cp source1 ... sourcen dest
8: *
9: * In the first case, dest must not be a directory (else we have a
10: * degenerate version of the second case). In the second case,
11: * dest must be a directory; the resulting objects will be
12: * named dest/source1 ... dest/sourcen.
13: *
14: * If any source is a directory, the tree structure under it
15: * will be (recursively) copied; this process will attempt to
16: * preserve any links that exist within the hierarchy.
17: */
18:
19: #include <stdio.h>
20: #include <sys/types.h>
21: #include <sys/stat.h>
22: #include <sys/dir.h>
23:
24: #define BLKSIZE BUFSIZ
25:
26: /* Maximum file tree nesting before we start closing and re-opening */
27: #define MAXLEVEL 8
28:
29: /* Non-zero if stat buffer b refers to a directory, zero otherwise */
30: #define isdir(b) (((b).st_mode & S_IFMT) == S_IFDIR)
31:
32: char *malloc(), *realloc(), *strcpy(), *strcat();
33: int iamsu;
34:
35: /* Final component of the path name by which we were invoked */
36: char *pgmname;
37:
38: main (argc, argv)
39: int argc;
40: char **argv;
41: {
42: int retcode;
43: char *dest, *destdir, *p, *q;
44: struct stat *argbufs, destbuf, statbuf, rootbuf;
45: int i;
46:
47: umask(0);
48: pgmname = argv[0];
49:
50: /*
51: * Start validity checking
52: */
53:
54: /* Check for too few arguments given */
55: if (argc < 3) {
56: fprintf (stderr, "usage: %s f1 f2 or %s f1 ... fn d\n",
57: pgmname, pgmname);
58: return 1;
59: }
60:
61: /*
62: * If the last argument is a non-directory, we require
63: * argc == 3 (in the case of: cp f1 f2)
64: */
65: dest = argv[argc-1];
66: destbuf.st_mode = 0; /* Force isdir(destbuf) false */
67: if ((stat (dest, &destbuf) < 0 || !isdir(destbuf)) && argc != 3) {
68: scream ("%s not a directory", dest);
69: return 1;
70: }
71:
72: /*
73: * Allocate storage for a stat buffer for each argument
74: * but the last. This will be used to check for trying to
75: * copy something into a subdirectory of itself.
76: */
77: argbufs = (struct stat *)
78: malloc ((unsigned) (sizeof (struct stat) * (argc-2)));
79: if (argbufs == NULL) {
80: scream ("insufficient storage", "");
81: return 1;
82: }
83: iamsu = (getuid() == 0);
84:
85: for (i = 0; i < argc-2; i++) {
86: if (stat (argv[i+1], &argbufs[i]) < 0) {
87: scream ("cannot access %s", argv[i+1]);
88: return 1;
89: }
90: }
91:
92: /*
93: * Now run back from the destination to the root directory,
94: * checking along the way for any matches with anything in
95: * the argbufs array. If we find any, we tried a forbidden
96: * operation. If the destination is not already a directory,
97: * we must start from its parent. Note that even in this case,
98: * we can't elide the test, because otherwise we wouldn't get
99: *
100: * cp . x
101: *
102: * This code also handles pathological but legal destinations
103: * properly; in particular note that the null string is a
104: * valid name for the current directory. Sigh.
105: *
106: * First figure out the name of the directory to check.
107: */
108: destdir = malloc ((unsigned) (strlen (dest) + 2));
109: if (destdir == NULL) {
110: scream ("insufficient storage", "");
111: return 1;
112: }
113: strcpy (destdir, dest);
114: if (!isdir (destbuf)) {
115: p = q = destdir;
116: while (*q) {
117: if (*q++ == '/')
118: p = q - 1;
119: }
120: if (p == destdir)
121: strcpy (destdir, ".");
122: else
123: *p = '\0';
124: }
125:
126: /* Now stat the root directory for later use */
127: if (stat ("/", &rootbuf) < 0) {
128: scream ("cannot access root directory", "");
129: return 1;
130: }
131:
132: /* Now run back from destdir to the root, checking against argbufs */
133: do {
134: if (stat (destdir, &statbuf) < 0) {
135: scream ("cannot stat %s", destdir);
136: return 1;
137: }
138: for (i = 0; i < argc-2; i++)
139: if (argbufs[i].st_dev == statbuf.st_dev
140: && argbufs[i].st_ino == statbuf.st_ino) {
141: fprintf (stderr, "%s: %s contains %s\n",
142: pgmname, argv[i+1], dest);
143: return 1;
144: }
145: destdir = realloc (destdir, (unsigned) (strlen(destdir) + 4));
146: if (destdir == NULL) {
147: scream ("insufficient storage", "");
148: return 1;
149: }
150: strcat (destdir, "/..");
151: } while (statbuf.st_dev != rootbuf.st_dev
152: || statbuf.st_ino != rootbuf.st_ino);
153: free (destdir);
154:
155: /*
156: * End of validity checking
157: */
158:
159: if (isdir (destbuf)) {
160: retcode = 0;
161: for (i=1; i<argc-1; i++) {
162:
163: char *p, *q;
164: char *xdest;
165:
166: p = q = argv[i];
167: while (*p)
168: if (*p++ == '/')
169: q = p;
170: xdest = malloc ((unsigned)
171: (strlen(dest) + strlen (q) + 2));
172: if (xdest != NULL) {
173: strcpy (xdest, dest);
174: strcat (xdest, "/");
175: strcat (xdest, q);
176: retcode |= copy (argv[i], &argbufs[i-1], xdest);
177: free (xdest);
178: } else {
179: scream ("no storage", "");
180: retcode = 1;
181: }
182: }
183: } else
184: retcode = copy (argv[1], &argbufs[0], dest);
185:
186: return retcode;
187: }
188:
189: /*
190: * Copy a file or directory. The source file name is in "source",
191: * and the destination file name is in "dest". "srcbuf" points to
192: * a stat buffer for the source.
193: */
194: int
195: copy (source, srcbuf, dest)
196: char *source, *dest;
197: struct stat *srcbuf;
198: {
199: int inf, outf, n;
200: char buffer[BLKSIZE];
201: struct stat newbuf, destbuf;
202: static int level = 0;
203: static short firstyell = 1;
204: struct {
205: long actime, modtime;
206: } utimbuf;
207: long time();
208: static struct linknode {
209: dev_t ln_dev;
210: ino_t ln_ino;
211: struct linknode *ln_next;
212: char *ln_name; /* variable */
213: } *ln_head = NULL, *lnp;
214:
215: if (isdir (*srcbuf)) {
216:
217: char *srcname, *destname;
218: FILE *dirfile;
219: struct direct dir;
220: long dirloc;
221: int retcode = 0;
222:
223: /*
224: * Copy from a directory. This will have to copy the
225: * tree structure recursively. Thus, we will create
226: * a new directory of an appropriate name, then
227: * copy everything in source to elements of dest.
228: */
229: if (stat (dest, &destbuf) < 0) {
230: if (mkdir (dest)) {
231: scream ("cannot create directory %s", dest);
232: return 1;
233: }
234: } else {
235: scream ("%s already exists", dest);
236: return 1;
237: }
238:
239: if ((dirfile = fopen (source, "r")) == NULL) {
240: scream ("cannot open %s", source);
241: return 1;
242: }
243:
244: srcname = malloc ((unsigned) (strlen(source) + DIRSIZ + 2));
245: destname = malloc ((unsigned) (strlen(dest) + DIRSIZ + 2));
246: if (srcname == NULL || destname == NULL) {
247: scream ("insufficient storage", "");
248: fclose(dirfile);
249: return 1;
250: }
251:
252: while (fread ((char *) & dir, sizeof (struct direct),
253: 1, dirfile) == 1)
254: if (dir.d_ino != 0 &&
255: strncmp (dir.d_name, ".", DIRSIZ) &&
256: strncmp (dir.d_name, "..", DIRSIZ)) {
257: strcpy (srcname, source);
258: strcat (srcname, "/");
259: strncat (srcname, dir.d_name, DIRSIZ);
260: strcpy (destname, dest);
261: strcat (destname, "/");
262: strncat (destname, dir.d_name, DIRSIZ);
263:
264: if (level > MAXLEVEL) {
265: dirloc = ftell (dirfile);
266: fclose (dirfile);
267: dirfile = NULL;
268: }
269:
270: if (lstat (srcname, &newbuf) < 0) {
271: scream ("cannot stat %s", srcname);
272: if(dirfile != NULL)
273: fclose(dirfile);
274: return 1;
275: }
276:
277: level++;
278: retcode |= copy (srcname, &newbuf, destname);
279: level--;
280:
281: if (level > MAXLEVEL) {
282: if ((dirfile = fopen (source, "r"))
283: == NULL) {
284: scream ("cannot reopen %s",
285: source);
286: return 1;
287: }
288: if(fseek(dirfile, dirloc, 0) == EOF) {
289: scream("can't seek on %s",
290: source);
291: fclose(dirfile);
292: return(1);
293: }
294: }
295: }
296:
297: free (srcname);
298: free (destname);
299: if(iamsu)
300: chown(dest, srcbuf->st_uid, srcbuf->st_gid);
301:
302: utimbuf.actime = time ((long *) NULL);
303: utimbuf.modtime = srcbuf->st_mtime;
304: utime (dest, &utimbuf);
305:
306: if (chmod (dest, srcbuf->st_mode & 07777) < 0) {
307: scream ("cannot chmod for %s", dest);
308: retcode = 1;
309: }
310: fclose(dirfile);
311: return retcode;
312: }
313:
314: /*
315: * Here when src is not a directory
316: */
317: if (stat (dest, &destbuf) >= 0
318: && srcbuf->st_dev == destbuf.st_dev
319: && srcbuf->st_ino == destbuf.st_ino) {
320: scream ("cannot copy %s to itself", source);
321: return 1;
322: }
323:
324: /*
325: * Check if the source (which is known to be a file) has
326: * more than one link. If not, all is easy. If so, we may
327: * be able to create a link rather than copying the source.
328: */
329: if (srcbuf->st_nlink > 1) {
330:
331: /* Hunt through the table of linked items for a match */
332: lnp = ln_head;
333: while (lnp != NULL &&
334: (lnp->ln_dev != srcbuf->st_dev
335: || lnp->ln_ino != srcbuf->st_ino)) {
336: lnp = lnp->ln_next;
337: }
338:
339: /* If match found, try a link */
340: if (lnp != NULL)
341: if (link (lnp->ln_name, dest)) {
342: scream ("cannot create %s link", dest);
343: return 1;
344: } else
345: return 0;
346:
347: /* No match found -- build a table entry */
348: lnp = (struct linknode *) malloc (sizeof (struct linknode));
349:
350: if (lnp == NULL) {
351: if (firstyell) {
352: scream ("link table too large", "");
353: firstyell = 0;
354: }
355: } else if ((lnp->ln_name = malloc ((unsigned) (strlen(dest)+1)))
356: == NULL) {
357: if (firstyell) {
358: scream ("link names too large", "");
359: firstyell = 0;
360: }
361: free ((char *) lnp);
362: } else {
363: lnp->ln_dev = srcbuf->st_dev;
364: lnp->ln_ino = srcbuf->st_ino;
365: strcpy (lnp->ln_name, dest);
366: lnp->ln_next = ln_head;
367: ln_head = lnp;
368: }
369: }
370:
371: /* Now copy the file, whether or not there was a link */
372: switch(srcbuf->st_mode & S_IFMT){
373: case S_IFLNK:{
374: int len;
375: char buf[1024];
376: len = readlink(source, buf, sizeof buf);
377: if(len<=0){
378: scream("can't read link for %s", source);
379: return 1;
380: }
381: if(symlink(buf, dest)!=0){
382: scream("can't write link for %s", dest);
383: return 1;
384: }
385: break;
386: }
387: case S_IFCHR:
388: case S_IFBLK:
389: if(mknod(dest, srcbuf->st_mode, srcbuf->st_rdev)!=0){
390: scream("can't mknod %s", dest);
391: return 1;
392: }
393: break;
394: default:
395: if ((outf = creat (dest, srcbuf->st_mode & 07777)) < 0) {
396: scream ("cannot create %s", dest);
397: return 1;
398: }
399: if ((inf = open (source, 0)) < 0) {
400: scream ("cannot open %s", source);
401: close(outf);
402: return 1;
403: }
404: while ((n = read (inf, buffer, BLKSIZE)) > 0) {
405: if (write (outf, buffer, n) != n) {
406: scream ("output error on %s", dest);
407: close (inf);
408: close (outf);
409: return 1;
410: }
411: }
412: if (n < 0) {
413: scream ("input error on %s", source);
414: close (inf);
415: close (outf);
416: return 1;
417: }
418: if (close (outf) < 0) {
419: scream ("cannot close %s", dest);
420: close (inf);
421: return 1;
422: }
423: if (close (inf) < 0) {
424: scream ("cannot close %s", source);
425: return 1;
426: }
427: break;
428: }
429: utimbuf.actime = srcbuf->st_atime;
430: utimbuf.modtime = srcbuf->st_mtime;
431: utime (dest, &utimbuf);
432: if(iamsu)
433: chown(dest, srcbuf->st_uid, srcbuf->st_gid);
434: return 0;
435: }
436:
437: int
438: mkdir(d)
439: char *d;
440: {
441: int status, pid, w;
442: /*w = open(".", 0);
443: printf("%d\t%s\n", w, d);
444: close(w);*/
445: switch (pid=fork()) {
446: case 0:
447: execl ("/bin/mkdir", "mkdir", d, 0);
448: /* No break */
449: case -1:
450: return 1;
451:
452: default:
453: do w = wait (&status);
454: while (w != pid && w > 0);
455: if (w == pid)
456: return status;
457: return w;
458: }
459: }
460:
461: scream (s1, s2)
462: char *s1, *s2;
463: {
464: fprintf (stderr, "%s: ", pgmname);
465: fprintf (stderr, s1, s2);
466: putc ('\n', stderr);
467: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.