📄 create.c
字号:
/* Create a tar archive. Copyright (C) 1988 Free Software FoundationThis file is part of GNU Tar.GNU Tar is free software; you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe Free Software Foundation; either version 1, or (at your option)any later version.GNU Tar is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.You should have received a copy of the GNU General Public Licensealong with GNU Tar; see the file COPYING. If not, write tothe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. *//* * Create a tar archive. * * Written 25 Aug 1985 by John Gilmore, ihnp4!hoptoad!gnu. * * @(#)create.c 1.36 11/6/87 - gnu */#include <sys/types.h>#include <sys/stat.h>#include <sys/file.h>#include <stdio.h>/* JF: this one is my fault *//* #include "utils.h" */#ifndef V7#include <fcntl.h>#endif#ifndef MSDOS#include <pwd.h>#include <grp.h>#endif#ifdef BSD42#include <sys/dir.h>#else#ifdef MSDOS#include <sys/dir.h>#else#ifdef USG#ifdef NDIR#include <ndir.h>#else#include <dirent.h>#endif#ifndef DIRECT#define direct dirent#endif#define DP_NAMELEN(x) strlen((x)->d_name)#else/* * FIXME: On other systems there is no standard place for the header file * for the portable directory access routines. Change the #include line * below to bring it in from wherever it is. */#include "ndir.h"#endif#endif#endif#ifndef DP_NAMELEN#define DP_NAMELEN(x) (x)->d_namlen#endif#ifdef USG#include <sys/sysmacros.h> /* major() and minor() defined here */#endif/* * V7 doesn't have a #define for this. */#ifndef O_RDONLY#define O_RDONLY 0#endif/* * Most people don't have a #define for this. */#ifndef O_BINARY#define O_BINARY 0#endif#include "tar.h"#include "port.h"extern union record *head; /* Points to current tape header */extern struct stat hstat; /* Stat struct corresponding */extern int head_standard; /* Tape header is in ANSI format */extern dev_t ar_dev;extern ino_t ar_ino;/* JF */extern struct name *gnu_list_name;/* * If there are no symbolic links, there is no lstat(). Use stat(). */#ifndef S_IFLNK#define lstat stat#endifextern char *malloc();extern char *strcpy();extern char *strncpy();extern void bzero();extern void bcopy();extern int errno;extern void print_header();union record *start_header();void finish_header();void finduname();void findgname();char *name_next();void to_oct();void dump_file();static nolinks; /* Gets set if we run out of RAM *//* * "Scratch" space to store the information about a sparse file before * writing the info into the header or extended header *//* struct sp_array *sparsearray;*//* number of elts storable in the sparsearray *//*int sparse_array_size = 10;*/voidcreate_archive(){ register char *p; char *name_from_list(); open_archive(0); /* Open for writing */ if(f_gnudump) { char buf[MAXNAMLEN],*q,*bufp; collect_and_sort_names(); while(p=name_from_list()) dump_file(p,-1); /* if(!f_dironly) { */ blank_name_list(); while(p=name_from_list()) { strcpy(buf,p); if(p[strlen(p)-1]!='/') strcat(buf,"/"); bufp=buf+strlen(buf); for(q=gnu_list_name->dir_contents;q && *q;q+=strlen(q)+1) { if(*q=='Y') { strcpy(bufp,q+1); dump_file(buf,-1); } } } /* } */ } else { p = name_next(1); if(!p) dump_file(".", -1); else { do dump_file(p, -1); while (p = name_next(1)); } } write_eot(); close_archive(); name_close();}/* * Dump a single file. If it's a directory, recurse. * Result is 1 for success, 0 for failure. * Sets global "hstat" to stat() output for this file. */voiddump_file (p, curdev) char *p; /* File name to dump */ int curdev; /* Device our parent dir was on */{ union record *header; char type; extern char *save_name; /* JF for multi-volume support */ extern long save_totsize; extern long save_sizeleft; union record *exhdr; char save_linkflag; extern time_t new_time; int sparse_ind = 0; if(f_confirm && !confirm("add",p)) return; /* * Use stat if following (rather than dumping) 4.2BSD's * symbolic links. Otherwise, use lstat (which, on non-4.2 * systems, is #define'd to stat anyway. */ if (0 != f_follow_links? stat(p, &hstat): lstat(p, &hstat)) {badperror: msg_perror("can't add file %s",p);badfile: errors++; return; } /* See if we only want new files, and check if this one is too old to put in the archive. */ if( f_new_files && !f_gnudump && new_time>hstat.st_mtime && (hstat.st_mode&S_IFMT)!=S_IFDIR && (f_new_files>1 || new_time>hstat.st_ctime)) { if(curdev<0) { msg("%s: is unchanged; not dumped",p); } return; } /* * See if we are crossing from one file system to another, * and avoid doing so if the user only wants to dump one file system. */ if (f_local_filesys && curdev >= 0 && curdev != hstat.st_dev) { if(f_verbose) msg("%s: is on a different filesystem; not dumped",p); return; } /* See if we are trying to dump the archive */ if(ar_dev && hstat.st_dev==ar_dev && hstat.st_ino==ar_ino) { msg("%s is the archive; not dumped",p); return; } /* * Check for multiple links. * * We maintain a list of all such files that we've written so * far. Any time we see another, we check the list and * avoid dumping the data again if we've done it once already. */ if (hstat.st_nlink > 1) switch (hstat.st_mode & S_IFMT) { register struct link *lp; case S_IFREG: /* Regular file */#ifdef S_IFCTG case S_IFCTG: /* Contigous file */#endif#ifdef S_IFCHR case S_IFCHR: /* Character special file */#endif#ifdef S_IFBLK case S_IFBLK: /* Block special file */#endif#ifdef S_IFIFO case S_IFIFO: /* Fifo special file */#endif /* First quick and dirty. Hashing, etc later FIXME */ for (lp = linklist; lp; lp = lp->next) { if (lp->ino == hstat.st_ino && lp->dev == hstat.st_dev) { char *link_name = lp->name; /* We found a link. */ hstat.st_size = 0; header = start_header(p, &hstat); if (header == NULL) goto badfile; while(!f_absolute_paths && *link_name == '/') { static int link_warn = 0; if (!link_warn) { msg("Removing leading / from absolute links"); link_warn++; } link_name++; } strcpy(header->header.linkname, link_name); header->header.linkflag = LF_LINK; finish_header(header); /* FIXME: Maybe remove from list after all links found? */ return; /* We dumped it */ } } /* Not found. Add it to the list of possible links. */ lp = (struct link *) malloc( (unsigned) (strlen(p) + sizeof(struct link) - NAMSIZ)); if (!lp) { if (!nolinks) { msg( "no memory for links, they will be dumped as separate files"); nolinks++; } } lp->ino = hstat.st_ino; lp->dev = hstat.st_dev; strcpy(lp->name, p); lp->next = linklist; linklist = lp; } /* * This is not a link to a previously dumped file, so dump it. */ switch (hstat.st_mode & S_IFMT) { case S_IFREG: /* Regular file */#ifdef S_IFCTG case S_IFCTG: /* Contiguous file */#endif { int f; /* File descriptor */ long bufsize, count; long sizeleft; register union record *start; int header_moved; char isextended = 0; int upperbound; int end_nulls = 0; header_moved = 0;#ifdef BSD42 if (f_sparse_files) { /* * JK - This is the test for sparseness: whether the * "size" of the file matches the number of blocks * allocated for it. If there is a smaller number * of blocks that would be necessary to accommodate * a file of this size, we have a sparse file, i.e., * at least one of those records in the file is just * a useless hole. */ if (hstat.st_size - (hstat.st_blocks * RECORDSIZE) > RECORDSIZE) { int filesize = hstat.st_size; register int i; printf("File is sparse: %s\n", p); header = start_header(p, &hstat); if (header == NULL) goto badfile; header->header.linkflag = LF_SPARSE; header_moved++; /* * Call the routine that figures out the * layout of the sparse file in question. * UPPERBOUND is the index of the last * element of the "sparsearray," i.e., * the number of elements it needed to * describe the file. */ upperbound = deal_with_sparse(p, header); /* * See if we'll need an extended header * later */ if (upperbound > SPARSE_IN_HDR-1) header->header.isextended++; /* * We store the "real" file size so * we can show that in case someone wants * to list the archive, i.e., tar tvf <file>. * It might be kind of disconcerting if the * shrunken file size was the one that showed * up. */ to_oct((long) hstat.st_size, 1+12, header->header.realsize); /* * This will be the new "size" of the * file, i.e., the size of the file * minus the records of holes that we're * skipping over. */ find_new_file_size(&filesize, upperbound); printf("File %s is now size %d\n", p, filesize); hstat.st_size = filesize; to_oct((long) filesize, 1+12, header->header.size);/* to_oct((long) end_nulls, 1+12, header->header.ending_blanks);*/ for (i = 0; i < SPARSE_IN_HDR; i++) { if (!sparsearray[i].numbytes) break; to_oct(sparsearray[i].offset, 1+12, header->header.sp[i].offset); to_oct(sparsearray[i].numbytes, 1+12, header->header.sp[i].numbytes); } } }#else upperbound=SPARSE_IN_HDR-1;#endif sizeleft = hstat.st_size; /* Don't bother opening empty, world readable files. */ if (sizeleft > 0 || 0444 != (0444 & hstat.st_mode)) { f = open(p, O_RDONLY|O_BINARY); if (f < 0) goto badperror; } else { f = -1; } /* If the file is sparse, we've already taken care of this */ if (!header_moved) { header = start_header(p, &hstat); if (header == NULL) { if(f>=0) (void)close(f); goto badfile; } }#ifdef S_IFCTG /* Mark contiguous files, if we support them */ if (f_standard && (hstat.st_mode & S_IFMT) == S_IFCTG) { header->header.linkflag = LF_CONTIG; }#endif isextended = header->header.isextended; save_linkflag = header->header.linkflag; finish_header(header); if (isextended) { int sum = 0; register int i;/* register union record *exhdr;*/ int arraybound = SPARSE_EXT_HDR; /* static */ int index_offset = SPARSE_IN_HDR; extend: exhdr = findrec(); if (exhdr == NULL) goto badfile; bzero(exhdr->charptr, RECORDSIZE); for (i = 0; i < SPARSE_EXT_HDR; i++) { if (i+index_offset > upperbound) break; to_oct((long) sparsearray[i+index_offset].numbytes, 1+12, exhdr->ext_hdr.sp[i].numbytes); to_oct((long) sparsearray[i+index_offset].offset, 1+12, exhdr->ext_hdr.sp[i].offset); } userec(exhdr);/* sum += i; if (sum < upperbound) goto extend;*/ if (index_offset+i < upperbound) { index_offset += i; exhdr->ext_hdr.isextended++; goto extend; } } if (save_linkflag == LF_SPARSE) { if (finish_sparse_file(f, &sizeleft, hstat.st_size, p)) goto padit; } else while (sizeleft > 0) { if(f_multivol) { save_name = p; save_sizeleft = sizeleft; save_totsize = hstat.st_size; } start = findrec(); bufsize = endofrecs()->charptr - start->charptr; if (sizeleft < bufsize) { /* Last read -- zero out area beyond */ bufsize = (int)sizeleft; count = bufsize % RECORDSIZE; if (count) bzero(start->charptr + sizeleft, (int)(RECORDSIZE - count)); } count = read(f, start->charptr, bufsize); if (count < 0) { msg_perror("read error at byte %ld, reading\ %d bytes, in file %s", hstat.st_size - sizeleft, bufsize,p); goto padit; } sizeleft -= count; /* This is nonportable (the type of userec's arg). */ userec(start+(count-1)/RECORDSIZE); if (count == bufsize) continue; msg( "file %s shrunk by %d bytes, padding with zeros.\n", p, sizeleft); goto padit; /* Short read */ } if(f_multivol) save_name = 0; if (f >= 0) (void)close(f); break; /* * File shrunk or gave error, pad out tape to match * the size we specified in the header. */ padit: while(sizeleft>0) { save_sizeleft=sizeleft; start=findrec(); bzero(start->charptr,RECORDSIZE); userec(start); sizeleft-=RECORDSIZE; } if(f_multivol) save_name=0; if(f>=0) (void)close(f); break;/* abort(); */ }#ifdef S_IFLNK case S_IFLNK: /* Symbolic link */ { int size; hstat.st_size = 0; /* Force 0 size on symlink */ header = start_header(p, &hstat); if (header == NULL) goto badfile; size = readlink(p, header->header.linkname, NAMSIZ); if (size < 0) goto badperror; if (size == NAMSIZ) { msg("symbolic link %s too long\n",p); break; } header->header.linkname[size] = '\0'; header->header.linkflag = LF_SYMLINK; finish_header(header); /* Nothing more to do to it */ } break;#endif case S_IFDIR: /* Directory */ { register DIR *dirp; register struct direct *d; char namebuf[NAMSIZ+2]; register int len; int our_device = hstat.st_dev; /* Build new prototype name */ strncpy(namebuf, p, sizeof (namebuf)); len = strlen(namebuf); while (len >= 1 && '/' == namebuf[len-1]) len--; /* Delete trailing slashes */ namebuf[len++] = '/'; /* Now add exactly one back */ namebuf[len] = '\0'; /* Make sure null-terminated */ /* * Output directory header record with permissions * FIXME, do this AFTER files, to avoid R/O dir problems? * If old archive format, don't write record at all. */ if (!f_oldarch) { hstat.st_size = 0; /* Force 0 size on dir */ /* * If people could really read standard archives, * this should be: (FIXME) header = start_header(f_standard? p: namebuf, &hstat); * but since they'd interpret LF_DIR records as * regular files, we'd better put the / on the name. */ header = start_header(namebuf, &hstat); if (header == NULL) goto badfile; /* eg name too long */ if (f_gnudump) header->header.linkflag = LF_DUMPDIR; else if (f_standard) header->header.linkflag = LF_DIR;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -