|
|
1.1 root 1: //
2: // Stuff to deal with tar-format files
3: //
4:
5: #include <sys/stat.h>
6: #include <stdlib.h>
7: #include <tar.h>
8: #include <stdio.h>
9: #include <fcntl.h>
10: #include <dirent.h>
11: #include <string.h>
12: #include <unistd.h>
13:
14: #include "buf.h"
15: #include "psxarc.h"
16: #include "tarhead.h"
17: #include "links.h"
18:
19: static unsigned long round_up(int, int);
20: static int tarAtoi(char *);
21: static void tarItoa(long, char *, size_t);
22: static void tar_dodir(PBUF pb, char *pchfile, struct stat *psb);
23: static char *modestring(PTAR_HEAD);
24: extern int fVerbose;
25:
26: void
27: TarRead(PBUF pb)
28: {
29: char name[155 + 1 + 100 + 1]; // (prefix '/' name '\0')
30: char linkname[TNAMSIZ + 1];
31: unsigned long size, i;
32: int fdout;
33: PTAR_HEAD buf = (PTAR_HEAD)pb->data;
34:
35:
36: if (0 != strcmp(buf->s.magic, TMAGIC)) {
37: fprintf(stderr, "%s: bad magic number\n", progname);
38: exit(1);
39: }
40:
41: name[sizeof(name) - 1] = '\0';
42:
43: do {
44: (void)strncpy(name, buf->s.prefix, 155);
45: // make sure 'name' is null-terminated.
46: name[155] = '\0';
47: if ('\0' != name[0]) {
48: (void)strcat(name, "/");
49: }
50: (void)strncat(name, buf->s.name, 100);
51:
52: if (fVerbose) {
53: printf("%s\n", name);
54: }
55:
56: if (DIRTYPE == buf->s.typeflag) {
57: if (-1 == mkdir(name, 0777)) {
58: fprintf(stderr, "%s: mkdir: ", progname);
59: perror(name);
60: }
61: bfill(pb);
62: continue;
63: }
64: if (FIFOTYPE == buf->s.typeflag) {
65: if (-1 == mkfifo(name, 0666)) {
66: fprintf(stderr, "%s: mkfifo: ", progname);
67: perror(name);
68: }
69: bfill(pb);
70: continue;
71: }
72: if (LNKTYPE == buf->s.typeflag) {
73: strncpy(linkname, buf->s.linkname, sizeof(linkname));
74: if (-1 == link(linkname, name)) {
75: fprintf(stderr, "%s: link %s, %s: ",
76: progname, linkname, name);
77: perror("");
78: continue;
79: }
80: }
81:
82: // regular file
83: if (-1 == (fdout = open(name, O_WRONLY | O_CREAT, 0666))) {
84: fprintf(stderr, "%s: open: ", progname);
85: perror(name);
86: continue;
87: }
88:
89: size = tarAtoi(buf->s.size);
90:
91: if (size > 0) {
92: bfill(pb);
93:
94: for (i = 0; i < size; ++i) {
95: int c;
96:
97: c = bgetc(pb);
98: write(fdout, &c, 1);
99: }
100: }
101: (void)close(fdout);
102:
103: bfill(pb);
104: } while (0 != buf->s.magic[0]);
105: }
106:
107: //
108: // TarWrite -- calls tar_dodir for directories, which calls back here.
109: //
110:
111: void
112: TarWrite(PBUF pb, char **ppchFiles, int count)
113: {
114: PTAR_HEAD pt;
115: auto struct stat statbuf;
116: int fdin;
117: int i;
118: unsigned chksum;
119:
120: // We reach into the buffer routines until it's time to write.
121: // Brutal but effective.
122:
123: pt = (PTAR_HEAD)pb->data;
124:
125: while (count > 0) {
126: int len;
127:
128: memset(pt, 0, sizeof(*pt));
129: strcpy(pt->s.magic, TMAGIC);
130: strncpy(pt->s.version, TVERSION, TVERSLEN);
131:
132: if (fVerbose) {
133: fprintf(stderr, "%s\n", *ppchFiles);
134: }
135: if (-1 == stat(*ppchFiles, &statbuf)) {
136: fprintf(stderr, "%s: stat: ");
137: perror(*ppchFiles);
138: continue;
139: }
140: len = strlen(*ppchFiles);
141: if (len > TNAMSIZ + 155 + 1) {
142: // the filename just won't fit. Do something
143: // reasonable.
144: } else if (len <= TNAMSIZ) {
145: strncpy(pt->s.name, *ppchFiles, TNAMSIZ);
146: } else {
147: char *pch;
148:
149: // We try to put as much of the filename as will fit
150: // into the 'name' portion, and the rest goes in
151: // the prefix. To do this, we start 101 characters
152: // from the end; if that character is a slash, we
153: // split the string there. If it's not, we split the
154: // string at the next slash to the right.
155:
156: pch = *ppchFiles + (len - TNAMSIZ - 1);
157: if ('/' != *pch) {
158: pch = strchr(pch, '/');
159: if (NULL == pch) {
160: // XXX.mjb: This filename has a trailing
161: // component more than 100 chars
162: // long. Do something reasonable.
163: --count;
164: ++ppchFiles;
165: continue;
166: }
167: }
168: *pch = '\0';
169: strncpy(pt->s.name, pch + 1, TNAMSIZ);
170: strncpy(pt->s.prefix, *ppchFiles, 155);
171: }
172:
173: //
174: // XXX.mjb: this assumes tar mode bits are the same as
175: // the POSIX implementation's mode bits. Should really
176: // call a function to convert between.
177: //
178: tarItoa(statbuf.st_mode & 0777, pt->s.mode,
179: sizeof(pt->s.mode));
180:
181: #if 0
182: tarItoa(statbuf.st_uid, pt->s.uid, sizeof(pt->s.uid));
183: tarItoa(statbuf.st_gid, pt->s.gid, sizeof(pt->s.gid));
184: tarItoa(statbuf.st_mtime, pt->s.mtime, sizeof(pt->s.mtime));
185: #endif
186:
187: if (S_ISDIR(statbuf.st_mode)) {
188: pt->s.typeflag = DIRTYPE;
189: memset(pt->s.size, '0', sizeof(pt->s.size));
190: // put the directory entry on the tape
191: bflush(pb);
192:
193: // put the directory contents on tape
194: tar_dodir(pb, *ppchFiles, &statbuf);
195:
196: ++ppchFiles;
197: --count;
198: continue;
199: }
200: if (S_ISFIFO(statbuf.st_mode)) {
201: pt->s.typeflag = FIFOTYPE;
202: memset(pt->s.size, '0', sizeof(pt->s.size));
203: bflush(pb);
204: ++ppchFiles;
205: --count;
206: continue;
207: }
208: if (statbuf.st_nlink > 1) {
209: PLINKFILE p;
210:
211: if (NULL != (p = GetLinkByIno(statbuf.st_ino))) {
212: pt->s.typeflag = LNKTYPE;
213: memset(pt->s.size, '0', sizeof(pt->s.size));
214: strncpy(pt->s.linkname, p->name, TNAMSIZ);
215: bflush(pb);
216:
217: ++ppchFiles;
218: --count;
219: continue;
220: }
221:
222: AddLinkList(&statbuf, *ppchFiles);
223: }
224:
225: pt->s.typeflag = REGTYPE;
226: tarItoa((int)statbuf.st_size, pt->s.size, sizeof(pt->s.size));
227:
228: //
229: // compute the checksum for the header
230: //
231:
232: memset(pt->s.chksum, ' ', sizeof(pt->s.chksum));
233: for (i = 0, chksum = 0; i < sizeof(pt->buf); ++i) {
234: chksum += pt->buf[i];
235: }
236: tarItoa(chksum, pt->s.chksum, sizeof(pt->s.chksum));
237:
238: fdin = open(*ppchFiles, O_RDONLY, 0);
239: if (-1 == fdin) {
240: fprintf(stderr, "%s: open: ");
241: perror(*ppchFiles);
242: continue;
243: }
244:
245: // write the header
246: bflush(pb);
247:
248: if (0 == statbuf.st_size) {
249: //
250: // special case: don't write any data blocks.
251: //
252: close(fdin);
253: ++ppchFiles;
254: --count;
255: continue;
256: }
257:
258: // copy the file data
259: // XXX.mjb: what should happen here if we find that we can't
260: // read the number of bytes we thought we'd be able to
261: // read? (The file could change size, or some kind
262: // of error could occur.) We can't leave the data too
263: // small, or we'll hose the rest of the tar file. So
264: // we write extra blocks of zeroes. What if the file
265: // turns out to be longer than expected? Print a
266: // warning and continue?
267:
268: memset(pb->data, 0, sizeof(pb->data));
269: while (statbuf.st_size > 0) {
270: int nbytes;
271: char c;
272:
273: nbytes = read(fdin, &c, 1);
274: if (-1 == nbytes) {
275: // error occurs before we have all the data.
276: }
277: bputc(pb, c);
278: --statbuf.st_size;
279: }
280: bflush(pb);
281: close(fdin);
282:
283: ++ppchFiles;
284: --count;
285: }
286: }
287:
288: void
289: TarList(PBUF pb)
290: {
291: PTAR_HEAD pt;
292: char name[155 + 1 + 100 + 1]; // "prefix / name \0"
293: unsigned long size, i;
294: char *pch;
295:
296: pt = (PTAR_HEAD)pb->data;
297:
298: do {
299: (void)strncpy(name, pt->s.prefix, 155);
300: name[155] = '\0';
301: if ('\0' != name[0]) {
302: (void)strcat(name, "/");
303: }
304: (void)strncat(name, pt->s.name, 100);
305:
306: size = tarAtoi(pt->s.size);
307:
308: if (!fVerbose) {
309: printf("%s\n", name);
310: } else {
311: pch = modestring(pt);
312: printf("%s %6ld %s\n", pch, size, name);
313: }
314:
315: size = round_up(size, 512);
316: for (i = 0; i < size; ++i) {
317: bfill(pb);
318: }
319: bfill(pb);
320: } while (0 != pt->s.magic[0]);
321: }
322:
323: //
324: // tarAtof -- translate tar-style octal strings to decimal
325: //
326: static int
327: tarAtoi(char *pch)
328: {
329: int num = 0;
330:
331: while ('\0' != *pch && ' ' != *pch) {
332: num = num * 8 + (*pch - '0');
333: ++pch;
334: }
335: return num;
336: }
337:
338: //
339: // tarItoa -- for writing numeric fields in tar headers.
340: void
341: tarItoa(long i, char *pch, size_t len)
342: {
343: // XXX.mjb: should check width < len
344: sprintf(pch, "%-o", i);
345: }
346:
347: //
348: // round_up -- round num up to the nearest multiple of m.
349: //
350: unsigned long
351: round_up(int num, int m)
352: {
353: return (num + (m - 1))/m;
354: }
355:
356: static void
357: tar_dodir(
358: PBUF pb, // the tar image file, being written
359: char *pchfile, // the directory file to be added to the archv
360: struct stat *psb // result of stat on the directory file.
361: )
362: {
363: DIR *dp;
364: struct dirent *dirent;
365: char *pch;
366:
367: dp = opendir(pchfile);
368: if (NULL == dp) {
369: fprintf(stderr, "%s: opendir: ", progname);
370: perror(pchfile);
371: return;
372: }
373: while (NULL != (dirent = readdir(dp))) {
374: if ('.' == dirent->d_name[0] &&
375: ('\0' == dirent->d_name[1] ||
376: 0 == strcmp(dirent->d_name, ".."))) {
377: continue;
378: }
379:
380: //
381: // Recurse. We append the file name read from the directory
382: // to the directory name we were given and call TarWrite to
383: // put it on the tape. It could be a directory, so we could
384: // end up back here. This means that we must allocate the
385: // space for the pathname dynamically. This seems like it
386: // will be a big time-waster.
387: //
388:
389: // strlen + 2: one extra for '/', one extra for null.
390: pch = malloc(strlen(pchfile) + strlen(dirent->d_name) + 2);
391: if (NULL == pch) {
392: fprintf(stderr, "%s: virtual memory exhausted\n",
393: progname);
394: exit(4);
395: }
396: strcpy(pch, pchfile);
397: strcat(pch, "/");
398: strcat(pch, dirent->d_name);
399:
400: TarWrite(pb, &pch, 1);
401: free(pch);
402: }
403: (void)closedir(dp);
404: }
405:
406: static char *
407: modestring(PTAR_HEAD pt)
408: {
409: static char sb[11];
410: unsigned long mask, mode;
411: char *rwx = "rwxrwxrwx";
412: char *string;
413:
414: sb[11] = '\0';
415: string = sb;
416:
417: switch (pt->s.typeflag) {
418: case LNKTYPE:
419: case REGTYPE:
420: string[0] = '-';
421: break;
422: case DIRTYPE:
423: string[0] = 'd';
424: break;
425: case FIFOTYPE:
426: string[0] = 'f';
427: break;
428: case SYMTYPE:
429: string[0] = 'l';
430: break;
431: case BLKTYPE:
432: string[0] = 'b';
433: break;
434: case CHRTYPE:
435: string[0] = 'c';
436: break;
437: case CONTTYPE:
438: string[0] = '=';
439: break;
440: default:
441: fprintf(stderr, "modestring shouldn't get here\n");
442: }
443: string++;
444:
445: mode = tarAtoi(pt->s.mode);
446:
447: for (mask = 0400; mask != 0; mask >>= 1) {
448: if (mode & mask) {
449: *string++ = *rwx++;
450: } else {
451: *string++ = '-';
452: rwx++;
453: }
454: }
455:
456: return sb;
457: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.