📄 create.c
字号:
/* If we're gnudumping, we aren't done yet so don't close it. */ if(!f_gnudump) finish_header(header); /* Done with directory header */ } /* Hack to remove "./" from the front of all the file names */ if (len == 2 && namebuf[0] == '.' && namebuf[1]=='/') { len = 0; } if(f_gnudump) { int sizeleft; int totsize; int bufsize; union record *start; int count; char *buf,*p_buf; buf=gnu_list_name->dir_contents; /* FOO */ totsize=0; for(p_buf=buf;p_buf && *p_buf;) { int tmp; tmp=strlen(p_buf)+1; totsize+=tmp; p_buf+=tmp; } totsize++; to_oct((long)totsize,1+12,header->header.size); finish_header(header); p_buf=buf; sizeleft=totsize; while(sizeleft>0) { if(f_multivol) { save_name=p; save_sizeleft=sizeleft; save_totsize=totsize; } start=findrec(); bufsize=endofrecs()->charptr - start->charptr; if(sizeleft<bufsize) { bufsize=sizeleft; count=bufsize%RECORDSIZE; if(count) bzero(start->charptr+sizeleft,RECORDSIZE-count); } bcopy(p_buf,start->charptr,bufsize); sizeleft-=bufsize; p_buf+=bufsize; userec(start+(bufsize-1)/RECORDSIZE); } if(f_multivol) save_name = 0; break; } /* Now output all the files in the directory */ /* if (f_dironly) break; /* Unless the cmdline said not to */ errno = 0; dirp = opendir(p); if (!dirp) { if (errno) { msg_perror ("can't open directory %s",p); } else { msg("error opening directory %s", p); } break; } /* Should speed this up by cd-ing into the dir, FIXME */ while (NULL != (d=readdir(dirp))) { /* Skip . and .. */ if(is_dot_or_dotdot(d->d_name)) continue; if (DP_NAMELEN(d) + len >= NAMSIZ) { namebuf[len]='\0'; msg("file name %s%s too long\n", namebuf, d->d_name); continue; } strcpy(namebuf+len, d->d_name); if(f_exclude && check_exclude(namebuf)) continue; dump_file(namebuf, our_device); } closedir(dirp); } break;#ifdef S_IFCHR case S_IFCHR: /* Character special file */ type = LF_CHR; goto easy;#endif#ifdef S_IFBLK case S_IFBLK: /* Block special file */ type = LF_BLK; goto easy;#endif#ifdef S_IFIFO case S_IFIFO: /* Fifo special file */ type = LF_FIFO; goto easy;#endif#ifdef S_IFSOCK# if S_IFSOCK != S_IFIFO case S_IFSOCK: /* Socket pretend its a fifo? */ type = LF_FIFO; goto easy;# endif#endif easy: if (!f_standard) goto unknown; hstat.st_size = 0; /* Force 0 size */ header = start_header(p, &hstat); if (header == NULL) goto badfile; /* eg name too long */ header->header.linkflag = type; if (type != LF_FIFO) { to_oct((long) major(hstat.st_rdev), 8, header->header.devmajor); to_oct((long) minor(hstat.st_rdev), 8, header->header.devminor); } finish_header(header); break; default: unknown: msg("%s: Unknown file type; file ignored.\n", p); break; }}intfinish_sparse_file(fd, sizeleft, fullsize, name) int fd; long *sizeleft, fullsize; char *name;{ union record *start; char tempbuf[RECORDSIZE]; int bufsize, sparse_ind = 0, count; long pos; while (*sizeleft > 0) { start = findrec(); bzero(start->charptr, RECORDSIZE); bufsize = sparsearray[sparse_ind].numbytes; if (!bufsize) { /* we blew it, maybe */ msg("Wrote %ld of %ld bytes to file %s", fullsize - *sizeleft, fullsize, name); break; } pos = lseek(fd, sparsearray[sparse_ind++].offset, 0); /* * If the number of bytes to be written here exceeds * the size of the temporary buffer, do it in steps. */ while (bufsize > RECORDSIZE) {/* if (amt_read) { count = read(fd, start->charptr+amt_read, RECORDSIZE-amt_read); bufsize -= RECORDSIZE - amt_read; amt_read = 0; userec(start); start = findrec(); bzero(start->charptr, RECORDSIZE); }*/ /* store the data */ count = read(fd, start->charptr, RECORDSIZE); if (count < 0) { msg_perror("read error at byte %ld, reading %d bytes, in file %s", fullsize - *sizeleft, bufsize, name); return 1; } bufsize -= count; *sizeleft -= count; userec(start); start = findrec(); bzero(start->charptr, RECORDSIZE); } clear_buffer(tempbuf); count = read(fd, tempbuf, bufsize); bcopy(tempbuf, start->charptr, RECORDSIZE); if (count < 0) { msg_perror("read error at byte %ld, reading %d bytes, in file %s", fullsize - *sizeleft, bufsize, name); return 1; }/* if (amt_read >= RECORDSIZE) { amt_read = 0; userec(start+(count-1)/RECORDSIZE); if (count != bufsize) { msg("file %s shrunk by %d bytes, padding with zeros.\n", name, sizeleft); return 1; } start = findrec(); } else amt_read += bufsize;*/ *sizeleft -= count; userec(start); } free(sparsearray);/* userec(start+(count-1)/RECORDSIZE);*/ return 0;}init_sparsearray(){ register int i; sp_array_size = 10; /* * Make room for our scratch space -- initially is 10 elts long */ sparsearray = (struct sp_array *) malloc(sp_array_size * sizeof(struct sp_array)); for (i = 0; i < sp_array_size; i++) { sparsearray[i].offset = 0; sparsearray[i].numbytes = 0; }}/* * Okay, we've got a sparse file on our hands -- now, what we need to do is * make a pass through the file and carefully note where any data is, i.e., * we want to find how far into the file each instance of data is, and how * many bytes are there. We store this information in the sparsearray, * which will later be translated into header information. For now, we use * the sparsearray as convenient storage. * * As a side note, this routine is a mess. If I could have found a cleaner * way to do it, I would have. If anyone wants to find a nicer way to do * this, feel free. */intdeal_with_sparse(name, header, nulls_at_end) char *name; union record *header; { long numbytes = 0; long offset = 0; long save_offset; int fd; int start, end; int end_nulls = 0; int current_size = hstat.st_size; int sparse_ind = 0, cc; char buf[RECORDSIZE]; int read_last_data = 0; /* did we just read the last record? */ int amidst_data = 0; header->header.isextended = 0; /* * Can't open the file -- this problem will be caught later on, * so just return. */ if ((fd = open(name, O_RDONLY)) < 0) return; init_sparsearray(); clear_buffer(buf); while ((cc = read(fd, buf, sizeof buf)) != 0) { if (sparse_ind > sp_array_size-1) { /* * realloc the scratch area, since we've run out of room -- */ sparsearray = (struct sp_array *) realloc(sparsearray, 2 * sp_array_size * (sizeof(struct sp_array))); sp_array_size *= 2; } if (cc == sizeof buf) { if (zero_record(buf)) { if (amidst_data) { sparsearray[sparse_ind++].numbytes = numbytes; amidst_data = 0; numbytes = 0; } offset += cc; } else { /* !zero_record(buf) */ if (!amidst_data) { amidst_data = 1; where_is_data(&start, &end, buf); numbytes += end - start; offset += start; sparsearray[sparse_ind].offset = offset; } else numbytes += cc; offset += cc; } } else if (cc < sizeof buf) { if (!zero_record(buf)) { if (!amidst_data) { amidst_data = 1; where_is_data(&start, &end, buf); /* In case there are nulls at the end that we need to remember */ if (end < cc) end = cc; numbytes += start - end; offset += start;/* end_nulls = RECORDSIZE - end;*/ } else { numbytes += cc;/* end_nulls = RECORDSIZE - end;*/ } sparsearray[sparse_ind].numbytes = numbytes; } /* else end_nulls = cc;*/ } clear_buffer(buf); } if (numbytes) sparsearray[sparse_ind].numbytes = numbytes; close(fd);/* printf("%d\n", end_nulls); *nulls_at_end = end_nulls;*/ return sparse_ind;}/* * Just zeroes out the buffer so we don't confuse ourselves with leftover * data. */clear_buffer(buf) char *buf;{ register int i; for (i = 0; i < RECORDSIZE; i++) buf[i] = '\0';}/* * JK - * This routine takes a character array, and tells where within that array * the data can be found. It skips over any zeros, and sets the first * non-zero point in the array to be the "start", and continues until it * finds non-data again, which is marked as the "end." This routine is * mainly for 1) seeing how far into a file we must lseek to data, given * that we have a sparse file, and 2) determining the "real size" of the * file, i.e., the number of bytes in the sparse file that are data, as * opposed to the zeros we are trying to skip. */where_is_data(from, to, buffer) int *from, *to; char *buffer;{ register int i = 0; register int save_to = *to; int amidst_data = 0; while (!buffer[i]) i++; *from = i; if (*from < 16) /* don't bother */ *from = 0; /* keep going to make sure there isn't more real data in this record */ while (i < RECORDSIZE) { if (!buffer[i]) { if (amidst_data) { save_to = i; amidst_data = 0; } i++; } else if (buffer[i]) { if (!amidst_data) amidst_data = 1; i++; } } if (i == RECORDSIZE) *to = i; else *to = save_to; }/* * Takes a recordful of data and basically cruises through it to see if * it's made *entirely* of zeros, returning a 0 the instant it finds * something that is a non-zero, i.e., useful data. */zero_record(buffer) char *buffer;{ register int i; for (i = 0; i < RECORDSIZE; i++) if (buffer[i] != '\000') return 0; return 1;}find_new_file_size(filesize, highest_index) int *filesize; int highest_index;{ register int i; *filesize = 0; for (i = 0; sparsearray[i].numbytes && i <= highest_index; i++) *filesize += sparsearray[i].numbytes;} /* * Make a header block for the file name whose stat info is st . * Return header pointer for success, NULL if the name is too long. */union record *start_header(name, st) char *name; register struct stat *st;{ register union record *header; header = (union record *) findrec(); bzero(header->charptr, sizeof(*header)); /* XXX speed up */ /* * Check the file name and put it in the record. */ if(!f_absolute_paths) { static int warned_once = 0;#ifdef MSDOS if(name[1]==':') { name+=2; if(!warned_once++) msg("Removing drive spec from names in the archive"); }#endif while ('/' == *name) { name++; /* Force relative path */ if (!warned_once++) msg("Removing leading / from absolute path names in the archive."); } } strcpy(header->header.name, name); if (header->header.name[NAMSIZ-1]) { msg("%s: name too long\n", name); return NULL; } to_oct((long) (st->st_mode & ~S_IFMT), 8, header->header.mode); to_oct((long) st->st_uid, 8, header->header.uid); to_oct((long) st->st_gid, 8, header->header.gid); to_oct((long) st->st_size, 1+12, header->header.size); to_oct((long) st->st_mtime, 1+12, header->header.mtime); /* header->header.linkflag is left as null */ if(f_gnudump) { to_oct((long) st->st_atime, 1+12, header->header.atime); to_oct((long) st->st_ctime, 1+12, header->header.ctime); }#ifndef NONAMES /* Fill in new Unix Standard fields if desired. */ if (f_standard) { header->header.linkflag = LF_NORMAL; /* New default */ strcpy(header->header.magic, TMAGIC); /* Mark as Unix Std */ finduname(header->header.uname, st->st_uid); findgname(header->header.gname, st->st_gid); }#endif return header;}/* * Finish off a filled-in header block and write it out. * We also print the file name and/or full info if verbose is on. */voidfinish_header(header) register union record *header;{ register int i, sum; register char *p; void bcopy(); bcopy(CHKBLANKS, header->header.chksum, sizeof(header->header.chksum)); sum = 0; p = header->charptr; for (i = sizeof(*header); --i >= 0; ) { /* * We can't use unsigned char here because of old compilers, * e.g. V7. */ sum += 0xFF & *p++; } /* * Fill in the checksum field. It's formatted differently * from the other fields: it has [6] digits, a null, then a * space -- rather than digits, a space, then a null. * We use to_oct then write the null in over to_oct's space. * The final space is already there, from checksumming, and * to_oct doesn't modify it. * * This is a fast way to do: * (void) sprintf(header->header.chksum, "%6o", sum); */ to_oct((long) sum, 8, header->header.chksum); header->header.chksum[6] = '\0'; /* Zap the space */ userec(header); if (f_verbose) { /* These globals are parameters to print_header, sigh */ head = header; /* hstat is already set up */ head_standard = f_standard; print_header(); } return;}/* * Quick and dirty octal conversion. * Converts long "value" into a "digs"-digit field at "where", * including a trailing space and room for a null. "digs"==3 means * 1 digit, a space, and room for a null. * * We assume the trailing null is already there and don't fill it in. * This fact is used by start_header and finish_header, so don't change it! * * This should be equivalent to: * (void) sprintf(where, "%*lo ", digs-2, value); * except that sprintf fills in the trailing null and we don't. */voidto_oct(value, digs, where) register long value; register int digs; register char *where;{ --digs; /* Trailing null slot is left alone */ where[--digs] = ' '; /* Put in the space, though */ /* Produce the digits -- at least one */ do { where[--digs] = '0' + (char)(value & 7); /* one octal digit */ value >>= 3; } while (digs > 0 && value != 0); /* Leading spaces, if necessary */ while (digs > 0) where[--digs] = ' ';}/* * Write the EOT record(s). * We actually zero at least one record, through the end of the block. * Old tar writes garbage after two zeroed records -- and PDtar used to. */write_eot(){ union record *p; int bufsize; void bzero(); p = findrec(); bufsize = endofrecs()->charptr - p->charptr; bzero(p->charptr, bufsize); userec(p);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -