Commit | Line | Data |
---|---|---|
c4f36ac6 WJ |
1 | /* Update a tar archive. |
2 | Copyright (C) 1988 Free Software Foundation | |
3 | ||
4 | This file is part of GNU Tar. | |
5 | ||
6 | GNU Tar is free software; you can redistribute it and/or modify | |
7 | it under the terms of the GNU General Public License as published by | |
8 | the Free Software Foundation; either version 1, or (at your option) | |
9 | any later version. | |
10 | ||
11 | GNU Tar is distributed in the hope that it will be useful, | |
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | GNU General Public License for more details. | |
15 | ||
16 | You should have received a copy of the GNU General Public License | |
17 | along with GNU Tar; see the file COPYING. If not, write to | |
18 | the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ | |
19 | ||
20 | /* JF implement the 'r' 'u' and 'A' options for tar. */ | |
21 | /* The 'A' option is my own invention: It means that the file-names are | |
22 | tar files, and they should simply be appended to the end of the archive. | |
23 | No attempt is made to block the reads from the args; if they're on raw | |
24 | tape or something like that, it'll probably lose. . . */ | |
25 | ||
26 | #include <sys/types.h> | |
27 | #include <sys/stat.h> | |
28 | #include <stdio.h> | |
29 | #include <errno.h> | |
30 | ||
31 | /* JF these includes are copied from create.c I'm not sure if they're right | |
32 | or not. */ | |
33 | #ifndef V7 | |
34 | #include <fcntl.h> | |
35 | #endif | |
36 | ||
37 | #ifndef MSDOS | |
38 | #include <pwd.h> | |
39 | #include <grp.h> | |
40 | #endif | |
41 | ||
42 | #ifdef USG | |
43 | #include <sys/sysmacros.h> /* major() and minor() defined here */ | |
44 | #endif | |
45 | ||
46 | /* | |
47 | * V7 doesn't have a #define for this. | |
48 | */ | |
49 | #ifndef O_RDONLY | |
50 | #define O_RDONLY 0 | |
51 | #endif | |
52 | ||
53 | /* | |
54 | * Most people don't have a #define for this. | |
55 | */ | |
56 | #ifndef O_BINARY | |
57 | #define O_BINARY 0 | |
58 | #endif | |
59 | ||
60 | #define STDIN 0 | |
61 | #define STDOUT 1 | |
62 | ||
63 | #include "tar.h" | |
64 | #include "port.h" | |
65 | #include "rmt.h" | |
66 | ||
67 | int time_to_start_writing = 0; /* We've hit the end of the old stuff, | |
68 | and its time to start writing new stuff | |
69 | to the tape. This involves seeking | |
70 | back one block and re-writing the current | |
71 | block (which has been changed). */ | |
72 | ||
73 | char *output_start; /* Pointer to where we started to write in | |
74 | the first block we write out. This is used | |
75 | if we can't backspace the output and have | |
76 | to null out the first part of the block */ | |
77 | ||
78 | extern void skip_file(); | |
79 | extern void skip_extended_headers(); | |
80 | ||
81 | extern union record *head; | |
82 | extern struct stat hstat; | |
83 | ||
84 | struct name *name_scan(); | |
85 | char *name_from_list(); | |
86 | ||
87 | /* Implement the 'r' (add files to end of archive), and 'u' (add files to | |
88 | end of archive if they arent there, or are more up to date than the | |
89 | version in the archive.) commands.*/ | |
90 | void | |
91 | update_archive() | |
92 | { | |
93 | int found_end = 0; | |
94 | int status = 3; | |
95 | int prev_status; | |
96 | char *p; | |
97 | struct name *name; | |
98 | extern void dump_file(); | |
99 | ||
100 | name_gather(); | |
101 | if(cmd_mode==CMD_UPDATE) | |
102 | name_expand(); | |
103 | open_archive(2); /* Open for updating */ | |
104 | ||
105 | do { | |
106 | prev_status=status; | |
107 | status=read_header(); | |
108 | switch(status) { | |
109 | case EOF: | |
110 | found_end=1; | |
111 | break; | |
112 | ||
113 | case 0: /* A bad record */ | |
114 | userec(head); | |
115 | switch(prev_status) { | |
116 | case 3: | |
117 | msg("This doesn't look like a tar archive."); | |
118 | /* FALL THROUGH */ | |
119 | case 2: | |
120 | case 1: | |
121 | msg("Skipping to next header"); | |
122 | case 0: | |
123 | break; | |
124 | } | |
125 | break; | |
126 | ||
127 | /* A good record */ | |
128 | case 1: | |
129 | /* printf("File %s\n",head->header.name); */ | |
130 | /* head->header.name[NAMSIZ-1]='\0'; */ | |
131 | if(cmd_mode==CMD_UPDATE && (name=name_scan(head->header.name))) { | |
132 | /* struct stat hstat; */ | |
133 | struct stat nstat; | |
134 | int head_standard; | |
135 | ||
136 | decode_header(head,&hstat,&head_standard,0); | |
137 | if(stat(head->header.name,&nstat)<0) { | |
138 | msg_perror("can't stat %s:",head->header.name); | |
139 | } else { | |
140 | if(hstat.st_mtime>=nstat.st_mtime) | |
141 | name->found++; | |
142 | } | |
143 | } | |
144 | userec(head); | |
145 | if (head->header.isextended) | |
146 | skip_extended_headers(); | |
147 | skip_file((long)hstat.st_size); | |
148 | break; | |
149 | ||
150 | case 2: | |
151 | ar_record=head; | |
152 | found_end = 1; | |
153 | break; | |
154 | } | |
155 | } while(!found_end); | |
156 | ||
157 | reset_eof(); | |
158 | time_to_start_writing = 1; | |
159 | output_start=ar_record->charptr; | |
160 | ||
161 | while(p=name_from_list()) { | |
162 | if(f_confirm && !confirm("add", p)) | |
163 | continue; | |
164 | if(cmd_mode==CMD_CAT) | |
165 | append_file(p); | |
166 | else | |
167 | dump_file(p,-1); | |
168 | } | |
169 | ||
170 | write_eot(); | |
171 | close_archive(); | |
172 | names_notfound(); | |
173 | } | |
174 | ||
175 | /* Catenate file p to the archive without creating a header for it. It had | |
176 | better be a tar file or the archive is screwed */ | |
177 | ||
178 | append_file(p) | |
179 | char *p; | |
180 | { | |
181 | int fd; | |
182 | struct stat statbuf; | |
183 | long bytes_left; | |
184 | union record *start; | |
185 | long bufsiz,count; | |
186 | ||
187 | if(0 != stat(p,&statbuf) || (fd=open(p,O_RDONLY|O_BINARY))<0) { | |
188 | msg_perror("can't open file %s",p); | |
189 | errors++; | |
190 | return; | |
191 | } | |
192 | ||
193 | bytes_left = statbuf.st_size; | |
194 | ||
195 | while(bytes_left>0) { | |
196 | start=findrec(); | |
197 | bufsiz=endofrecs()->charptr - start->charptr; | |
198 | if(bytes_left < bufsiz) { | |
199 | bufsiz = bytes_left; | |
200 | count = bufsiz % RECORDSIZE; | |
201 | if(count) | |
202 | bzero(start->charptr + bytes_left,(int)(RECORDSIZE-count)); | |
203 | } | |
204 | count=read(fd,start->charptr,bufsiz); | |
205 | if(count<0) { | |
206 | msg_perror("read error at byte %ld reading %d bytes in file %s",statbuf.st_size-bytes_left,bufsiz,p); | |
207 | exit(EX_ARGSBAD); /* FOO */ | |
208 | } | |
209 | bytes_left-=count; | |
210 | userec(start+(count-1)/RECORDSIZE); | |
211 | if(count!=bufsiz) { | |
212 | msg("%s: file shrunk by %d bytes, yark!",p,bytes_left); | |
213 | abort(); | |
214 | } | |
215 | } | |
216 | (void)close(fd); | |
217 | } | |
218 | ||
219 | #ifdef DONTDEF | |
220 | bprint(fp,buf,num) | |
221 | FILE *fp; | |
222 | char *buf; | |
223 | { | |
224 | int c; | |
225 | ||
226 | if(num==0 || num==-1) | |
227 | return; | |
228 | fputs(" '",fp); | |
229 | while(num--) { | |
230 | c= *buf++; | |
231 | if(c=='\\') fputs("\\\\",fp); | |
232 | else if(c>=' ' && c<='~') | |
233 | putc(c,fp); | |
234 | else switch(c) { | |
235 | case '\n': | |
236 | fputs("\\n",fp); | |
237 | break; | |
238 | case '\r': | |
239 | fputs("\\r",fp); | |
240 | break; | |
241 | case '\b': | |
242 | fputs("\\b",fp); | |
243 | break; | |
244 | case '\0': | |
245 | /* fputs("\\-",fp); */ | |
246 | break; | |
247 | default: | |
248 | fprintf(fp,"\\%03o",c); | |
249 | break; | |
250 | } | |
251 | } | |
252 | fputs("'\n",fp); | |
253 | } | |
254 | #endif | |
255 | ||
256 | int number_of_blocks_read = 0; | |
257 | ||
258 | int number_of_new_records = 0; | |
259 | int number_of_records_needed = 0; | |
260 | ||
261 | union record *new_block = 0; | |
262 | union record *save_block = 0; | |
263 | ||
264 | void | |
265 | junk_archive() | |
266 | { | |
267 | int found_stuff = 0; | |
268 | int status = 3; | |
269 | int prev_status; | |
270 | struct name *name; | |
271 | ||
272 | /* int dummy_head; */ | |
273 | int number_of_records_to_skip = 0; | |
274 | int number_of_records_to_keep = 0; | |
275 | int number_of_kept_records_in_block; | |
276 | int sub_status; | |
277 | extern int write_archive_to_stdout; | |
278 | ||
279 | /* fprintf(stderr,"Junk files\n"); */ | |
280 | name_gather(); | |
281 | open_archive(2); | |
282 | ||
283 | while(!found_stuff) { | |
284 | prev_status=status; | |
285 | status=read_header(); | |
286 | switch(status) { | |
287 | case EOF: | |
288 | found_stuff = 1; | |
289 | break; | |
290 | ||
291 | case 0: | |
292 | userec(head); | |
293 | switch(prev_status) { | |
294 | case 3: | |
295 | msg("This doesn't look like a tar archive."); | |
296 | /* FALL THROUGH */ | |
297 | case 2: | |
298 | case 1: | |
299 | msg("Skipping to next header"); | |
300 | /* FALL THROUGH */ | |
301 | case 0: | |
302 | break; | |
303 | } | |
304 | break; | |
305 | ||
306 | case 1: | |
307 | /* head->header.name[NAMSIZ-1] = '\0'; */ | |
308 | /* fprintf(stderr,"file %s\n",head->header.name); */ | |
309 | if((name=name_scan(head->header.name))==(struct name *)0) { | |
310 | userec(head); | |
311 | /* fprintf(stderr,"Skip %ld\n",(long)(hstat.st_size)); */ | |
312 | if (head->header.isextended) | |
313 | skip_extended_headers(); | |
314 | skip_file((long)(hstat.st_size)); | |
315 | break; | |
316 | } | |
317 | name->found = 1; | |
318 | found_stuff = 2; | |
319 | break; | |
320 | ||
321 | case 2: | |
322 | found_stuff = 1; | |
323 | break; | |
324 | } | |
325 | } | |
326 | /* fprintf(stderr,"Out of first loop\n"); */ | |
327 | ||
328 | if(found_stuff!=2) { | |
329 | write_eot(); | |
330 | close_archive(); | |
331 | names_notfound(); | |
332 | return; | |
333 | } | |
334 | ||
335 | if(write_archive_to_stdout) | |
336 | write_archive_to_stdout = 0; | |
337 | new_block = (union record *)malloc(blocksize); | |
338 | if(new_block==0) { | |
339 | msg("Can't allocate secondary block of %d bytes",blocksize); | |
340 | exit(EX_SYSTEM); | |
341 | } | |
342 | ||
343 | /* Save away records before this one in this block */ | |
344 | number_of_new_records=ar_record-ar_block; | |
345 | number_of_records_needed = blocking - number_of_new_records; | |
346 | if(number_of_new_records) | |
347 | bcopy((void *)ar_block,(void *)new_block,(number_of_new_records)*RECORDSIZE); | |
348 | ||
349 | /* fprintf(stderr,"Saved %d recs, need %d more\n",number_of_new_records,number_of_records_needed); */ | |
350 | userec(head); | |
351 | if (head->header.isextended) | |
352 | skip_extended_headers(); | |
353 | skip_file((long)(hstat.st_size)); | |
354 | found_stuff=0; | |
355 | /* goto flush_file; */ | |
356 | ||
357 | for(;;) { | |
358 | /* Fill in a block */ | |
359 | /* another_file: */ | |
360 | if(ar_record==ar_last) { | |
361 | /* fprintf(stderr,"New block\n"); */ | |
362 | flush_archive(); | |
363 | number_of_blocks_read++; | |
364 | } | |
365 | sub_status = read_header(); | |
366 | /* fprintf(stderr,"Header type %d\n",sub_status); */ | |
367 | ||
368 | if(sub_status==2 && f_ignorez) { | |
369 | userec(head); | |
370 | continue; | |
371 | } | |
372 | if(sub_status==EOF || sub_status==2) { | |
373 | found_stuff = 1; | |
374 | bzero(new_block[number_of_new_records].charptr,RECORDSIZE*number_of_records_needed); | |
375 | number_of_new_records+=number_of_records_needed; | |
376 | number_of_records_needed = 0; | |
377 | write_block(0); | |
378 | break; | |
379 | } | |
380 | ||
381 | if(sub_status==0) { | |
382 | msg("Deleting non-header from archive."); | |
383 | userec(head); | |
384 | continue; | |
385 | } | |
386 | ||
387 | /* Found another header. Yipee! */ | |
388 | /* head->header.name[NAMSIZ-1] = '\0'; */ | |
389 | /* fprintf(stderr,"File %s ",head->header.name); */ | |
390 | if(name=name_scan(head->header.name)) { | |
391 | name->found = 1; | |
392 | /* fprintf(stderr,"Flush it\n"); */ | |
393 | /* flush_file: */ | |
394 | /* decode_header(head,&hstat,&dummy_head,0); */ | |
395 | userec(head); | |
396 | number_of_records_to_skip=(hstat.st_size+RECORDSIZE-1)/RECORDSIZE; | |
397 | /* fprintf(stderr,"Flushing %d recs from %s\n",number_of_records_to_skip,head->header.name); */ | |
398 | ||
399 | while(ar_last-ar_record<=number_of_records_to_skip) { | |
400 | ||
401 | /* fprintf(stderr,"Block: %d <= %d ",ar_last-ar_record,number_of_records_to_skip); */ | |
402 | number_of_records_to_skip -= (ar_last - ar_record); | |
403 | flush_archive(); | |
404 | number_of_blocks_read++; | |
405 | /* fprintf(stderr,"Block %d left\n",number_of_records_to_skip); */ | |
406 | } | |
407 | ar_record+=number_of_records_to_skip; | |
408 | /* fprintf(stderr,"Final %d\n",number_of_records_to_skip); */ | |
409 | number_of_records_to_skip = 0; | |
410 | continue; | |
411 | } | |
412 | ||
413 | /* copy_header: */ | |
414 | new_block[number_of_new_records]= *head; | |
415 | number_of_new_records++; | |
416 | number_of_records_needed--; | |
417 | number_of_records_to_keep=(hstat.st_size+RECORDSIZE-1)/RECORDSIZE; | |
418 | userec(head); | |
419 | if(number_of_records_needed==0) | |
420 | write_block(1); | |
421 | /* copy_data: */ | |
422 | number_of_kept_records_in_block = ar_last - ar_record; | |
423 | if(number_of_kept_records_in_block > number_of_records_to_keep) | |
424 | number_of_kept_records_in_block = number_of_records_to_keep; | |
425 | ||
426 | /* fprintf(stderr,"Need %d kept_in %d keep %d\n",blocking,number_of_kept_records_in_block,number_of_records_to_keep); */ | |
427 | ||
428 | while(number_of_records_to_keep) { | |
429 | int n; | |
430 | ||
431 | if(ar_record==ar_last) { | |
432 | /* fprintf(stderr,"Flush. . .\n"); */ | |
433 | fl_read(); | |
434 | number_of_blocks_read++; | |
435 | ar_record=ar_block; | |
436 | number_of_kept_records_in_block = blocking; | |
437 | if(number_of_kept_records_in_block > number_of_records_to_keep) | |
438 | number_of_kept_records_in_block = number_of_records_to_keep; | |
439 | } | |
440 | n = number_of_kept_records_in_block; | |
441 | if(n>number_of_records_needed) | |
442 | n = number_of_records_needed; | |
443 | ||
444 | /* fprintf(stderr,"Copying %d\n",n); */ | |
445 | bcopy((void *)ar_record, (void *)(new_block+number_of_new_records), n*RECORDSIZE); | |
446 | number_of_new_records += n; | |
447 | number_of_records_needed -= n; | |
448 | ar_record += n; | |
449 | number_of_records_to_keep -= n; | |
450 | number_of_kept_records_in_block -= n; | |
451 | /* fprintf(stderr,"Now new %d need %d keep %d keep_in %d rec %d/%d\n", | |
452 | number_of_new_records,number_of_records_needed,number_of_records_to_keep, | |
453 | number_of_kept_records_in_block,ar_record-ar_block,ar_last-ar_block); */ | |
454 | ||
455 | if(number_of_records_needed == 0) { | |
456 | write_block(1); | |
457 | } | |
458 | } | |
459 | } | |
460 | ||
461 | write_eot(); | |
462 | close_archive(); | |
463 | names_notfound(); | |
464 | } | |
465 | ||
466 | write_block(f) | |
467 | { | |
468 | /* fprintf(stderr,"Write block\n"); */ | |
469 | /* We've filled out a block. Write it out. */ | |
470 | ||
471 | /* Backspace back to where we started. . . */ | |
472 | if(archive!=STDIN) | |
473 | (void)move_arch(-(number_of_blocks_read+1)); | |
474 | ||
475 | save_block = ar_block; | |
476 | ar_block = new_block; | |
477 | ||
478 | if(archive==STDIN) | |
479 | archive=STDOUT; | |
480 | fl_write(); | |
481 | ||
482 | if(archive==STDOUT) | |
483 | archive=STDIN; | |
484 | ar_block = save_block; | |
485 | ||
486 | if(f) { | |
487 | /* Move the tape head back to where we were */ | |
488 | if(archive!=STDIN) | |
489 | (void)move_arch(number_of_blocks_read); | |
490 | number_of_blocks_read--; | |
491 | } | |
492 | ||
493 | number_of_records_needed = blocking; | |
494 | number_of_new_records = 0; | |
495 | } | |
496 | ||
497 | /* Move archive descriptor by n blocks worth. If n is positive we move | |
498 | forward, else we move negative. If its a tape, MTIOCTOP had better | |
499 | work. If its something else, we try to seek on it. If we can't | |
500 | seek, we lose! */ | |
501 | move_arch(n) | |
502 | { | |
503 | long cur; | |
504 | extern int errno; | |
505 | ||
506 | #ifdef MTIOCTOP | |
507 | struct mtop t; | |
508 | int er; | |
509 | ||
510 | if(n>0) { | |
511 | t.mt_op = MTFSR; | |
512 | t.mt_count = n; | |
513 | } else { | |
514 | t.mt_op = MTBSR; | |
515 | t.mt_count = -n; | |
516 | } | |
517 | if((er=rmtioctl(archive,MTIOCTOP,&t))>=0) | |
518 | return 1; | |
519 | if(errno==EIO && (er=rmtioctl(archive,MTIOCTOP,&t))>=0) | |
520 | return 1; | |
521 | #endif | |
522 | ||
523 | cur=rmtlseek(archive,0L,1); | |
524 | cur+=blocksize*n; | |
525 | ||
526 | /* fprintf(stderr,"Fore to %x\n",cur); */ | |
527 | if(rmtlseek(archive,cur,0)!=cur) { | |
528 | /* Lseek failed. Try a different method */ | |
529 | msg("Couldn't re-position archive file."); | |
530 | exit(EX_BADARCH); | |
531 | } | |
532 | return 3; | |
533 | } | |
534 |