X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=src%2Fcreate.c;h=6808fba926869e09f7d07726ddd292e640fe8964;hb=ebeb3d4f3058b7bbb92e7ea797cf938ec03ee2ed;hp=a0c76289e3cc773d33678910344c87fd7009c12d;hpb=7653d2f6b8bd48b64f03e78d0f8754f58b55f920;p=chaz%2Ftar diff --git a/src/create.c b/src/create.c index a0c7628..6808fba 100644 --- a/src/create.c +++ b/src/create.c @@ -21,11 +21,6 @@ #include "system.h" -#if !MSDOS -# include -# include -#endif - #if HAVE_UTIME_H # include #else @@ -41,11 +36,6 @@ struct utimbuf #include "common.h" #include -#ifndef MSDOS -extern dev_t ar_dev; -extern ino_t ar_ino; -#endif - struct link { dev_t dev; @@ -363,24 +353,54 @@ write_eot (void) set_next_block_after (pointer); } -/* Write a GNUTYPE_LONGLINK or GNUTYPE_LONGNAME block. */ +/* Write a "private" header */ +static union block * +start_private_header (const char *name, size_t size) +{ + time_t t; + union block *header = find_next_block (); + + memset (header->buffer, 0, sizeof (union block)); + + strncpy (header->header.name, name, NAME_FIELD_SIZE); + header->header.name[NAME_FIELD_SIZE - 1] = '\0'; + OFF_TO_CHARS (size, header->header.size); + + time (&t); + TIME_TO_CHARS (t, header->header.mtime); + MODE_TO_CHARS (S_IFREG|S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH, header->header.mode); + UID_TO_CHARS (getuid (), header->header.uid); + GID_TO_CHARS (getgid (), header->header.gid); + MAJOR_TO_CHARS (0, header->header.devmajor); + MAJOR_TO_CHARS (0, header->header.devminor); + strncpy (header->header.magic, TMAGIC, TMAGLEN); + strncpy (header->header.version, TVERSION, TVERSLEN); + return header; +} -/* FIXME: Cross recursion between start_header and write_long! */ +/* Create a new header and store there at most NAME_FIELD_SIZE bytes of + the file name */ -static union block *start_header (const char *, struct tar_stat_info *); +static union block * +write_short_name (struct tar_stat_info *st) +{ + union block *header = find_next_block (); + memset (header->buffer, 0, sizeof (union block)); + + strncpy (header->header.name, st->file_name, NAME_FIELD_SIZE); + header->header.name[NAME_FIELD_SIZE - 1] = '\0'; + return header; +} +/* Write a GNUTYPE_LONGLINK or GNUTYPE_LONGNAME block. */ static void -write_long (const char *p, char type) +write_gnu_long_link (const char *p, char type) { size_t size = strlen (p) + 1; size_t bufsize; union block *header; - struct tar_stat_info foo; - - memset (&foo, 0, sizeof foo); - foo.stat.st_size = size; - header = start_header ("././@LongLink", &foo); + header = start_private_header ("././@LongLink", size); header->header.typeflag = type; finish_header (header, -1); @@ -402,7 +422,104 @@ write_long (const char *p, char type) set_next_block_after (header + (size - 1) / BLOCKSIZE); } -/* NOTE: Cross recursion between start_header and write_extended */ +static size_t +split_long_name (const char *name, size_t length) +{ + size_t i; + + if (length > PREFIX_FIELD_SIZE) + length = PREFIX_FIELD_SIZE+2; + for (i = length - 1; i > 0; i--) + if (ISSLASH (name[i])) + break; + return i; +} + +static union block * +write_ustar_long_name (const char *name) +{ + size_t length = strlen (name); + size_t i; + union block *header; + + if (length > PREFIX_FIELD_SIZE + NAME_FIELD_SIZE + 1) + { + WARN ((0, 0, _("%s: file name is too long (max %d); not dumped"), + quotearg_colon (name), + PREFIX_FIELD_SIZE + NAME_FIELD_SIZE + 1)); + return NULL; + } + + i = split_long_name (name, length); + if (i == 0) + { + WARN ((0, 0, + _("%s: file name is too long (cannot be split); not dumped"), + quotearg_colon (name), + PREFIX_FIELD_SIZE + NAME_FIELD_SIZE + 1)); + return NULL; + } + + header = find_next_block (); + memset (header->buffer, 0, sizeof (header->buffer)); + memcpy (header->header.prefix, name, i); + memcpy (header->header.name, name + i + 1, length - i); + + return header; +} + +/* Write a long link name, depending on the current archive format */ +static void +write_long_link (struct tar_stat_info *st) +{ + switch (archive_format) + { + case POSIX_FORMAT: + xheader_store ("linkpath", st); + break; + + case V7_FORMAT: /* old V7 tar format */ + case USTAR_FORMAT: + case STAR_FORMAT: + WARN ((0, 0, + _("%s: link name is too long; not dumped"), + quotearg_colon (st->link_name))); + break; + + case OLDGNU_FORMAT: + case GNU_FORMAT: + write_gnu_long_link (st->link_name, GNUTYPE_LONGLINK); + break; + + default: + abort(); /*FIXME*/ + } +} + +static union block * +write_long_name (struct tar_stat_info *st) +{ + switch (archive_format) + { + case POSIX_FORMAT: + xheader_store ("path", st); + break; + + case V7_FORMAT: + case USTAR_FORMAT: + case STAR_FORMAT: + return write_ustar_long_name (st->file_name); + + case OLDGNU_FORMAT: + case GNU_FORMAT: + write_gnu_long_link (st->file_name, GNUTYPE_LONGNAME); + break; + + default: + abort(); /*FIXME*/ + } + return write_short_name (st); +} static union block * write_extended (union block *old_header, char type) @@ -413,20 +530,14 @@ write_extended (union block *old_header, char type) char *p; if (extended_header.buffer || extended_header.stk == NULL) - return old_header; /* Prevent recursion */ + return old_header; xheader_finish (&extended_header); size = extended_header.size; - memset (&foo, 0, sizeof foo); - foo.stat.st_mode = S_IFREG|S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH; - time (&foo.stat.st_ctime); - foo.stat.st_atime = foo.stat.st_ctime; - foo.stat.st_mtime = foo.stat.st_ctime; - foo.stat.st_size = size; memcpy (hp.buffer, old_header, sizeof (hp)); - header = start_header ("././@PaxHeader", &foo); + header = start_private_header ("././@PaxHeader", size); header->header.typeflag = type; finish_header (header, -1); @@ -449,13 +560,22 @@ write_extended (union block *old_header, char type) set_next_block_after (header); } while (size > 0); - + xheader_destroy (&extended_header); header = find_next_block (); memcpy (header, &hp.buffer, sizeof (hp.buffer)); return header; } +static union block * +write_header_name (struct tar_stat_info *st) +{ + if (NAME_FIELD_SIZE <= strlen (st->file_name)) + return write_long_name (st); + else + return write_short_name (st); +} + /* Header handling. */ @@ -469,23 +589,13 @@ start_header (const char *name, struct tar_stat_info *st) name = safer_name_suffix (name, 0); assign_string (&st->file_name, name); - - if (sizeof header->header.name <= strlen (name)) - { - if (archive_format == POSIX_FORMAT) - xheader_store ("path", st); - else - write_long (name, GNUTYPE_LONGNAME); - } - - header = find_next_block (); - memset (header->buffer, 0, sizeof (union block)); + + header = write_header_name (st); + if (!header) + return NULL; assign_string (¤t_stat_info.file_name, name); - strncpy (header->header.name, name, NAME_FIELD_SIZE); - header->header.name[NAME_FIELD_SIZE - 1] = '\0'; - /* Override some stat fields, if requested to do so. */ if (owner_option != (uid_t) -1) @@ -519,7 +629,7 @@ start_header (const char *name, struct tar_stat_info *st) above, thus making GNU tar both a universal donor and a universal acceptor for Paul's test. */ - if (archive_format == V7_FORMAT) + if (archive_format == V7_FORMAT || archive_format == USTAR_FORMAT) MODE_TO_CHARS (st->stat.st_mode & MODE_ALL, header->header.mode); else MODE_TO_CHARS (st->stat.st_mode, header->header.mode); @@ -589,7 +699,8 @@ start_header (const char *name, struct tar_stat_info *st) break; case POSIX_FORMAT: - case GNU_FORMAT: + case USTAR_FORMAT: + case GNU_FORMAT: /*FIXME?*/ strncpy (header->header.magic, TMAGIC, TMAGLEN); strncpy (header->header.version, TVERSION, TVERSLEN); break; @@ -974,6 +1085,15 @@ compare_links (void const *entry1, void const *entry2) return ((link1->dev ^ link2->dev) | (link1->ino ^ link2->ino)) == 0; } +/* Copy at most LEN bytes from SRC to DST. Terminate with NUL unless + SRC is LEN characters long */ +static void +tar_copy_str (char *dst, const char *src, size_t len) +{ + dst[len-1] = 0; + strncpy (dst, src, len); +} + /* Table of all non-directories that we've written so far. Any time we see another, we check the table and avoid dumping the data again if we've done it once already. */ @@ -1048,16 +1168,13 @@ dump_file (char *p, int top_level, dev_t parent_device) return; } -#if !MSDOS /* See if we are trying to dump the archive. */ - - if (ar_dev && current_stat_info.stat.st_dev == ar_dev && current_stat_info.stat.st_ino == ar_ino) + if (sys_file_is_archive (¤t_stat_info)) { WARN ((0, 0, _("%s: file is the archive; not dumped"), quotearg_colon (p))); return; } -#endif if (S_ISDIR (current_stat_info.stat.st_mode)) { @@ -1107,17 +1224,10 @@ dump_file (char *p, int top_level, dev_t parent_device) block_ordinal = current_block_ordinal (); current_stat_info.stat.st_size = 0; /* force 0 size on dir */ - /* FIXME: If people could really read standard archives, this - should be: - - header - = start_header (standard_option ? p : namebuf, ¤t_stat_info); - - but since they'd interpret DIRTYPE blocks as regular - files, we'd better put the / on the name. */ - header = start_header (namebuf, ¤t_stat_info); - + if (!header) + return; + if (incremental_option) header->header.typeflag = GNUTYPE_DUMPDIR; else /* if (standard_option) */ @@ -1245,16 +1355,16 @@ dump_file (char *p, int top_level, dev_t parent_device) dup->nlink--; block_ordinal = current_block_ordinal (); - if (NAME_FIELD_SIZE <= strlen (link_name)) - write_long (link_name, GNUTYPE_LONGLINK); assign_string (¤t_stat_info.link_name, link_name); + if (NAME_FIELD_SIZE < strlen (link_name)) + write_long_link (¤t_stat_info); current_stat_info.stat.st_size = 0; header = start_header (p, ¤t_stat_info); - strncpy (header->header.linkname, link_name, NAME_FIELD_SIZE); - - /* Force null termination. */ - header->header.linkname[NAME_FIELD_SIZE - 1] = 0; + if (!header) + return; + tar_copy_str (header->header.linkname, link_name, + NAME_FIELD_SIZE); header->header.typeflag = LNKTYPE; finish_header (header, block_ordinal); @@ -1327,6 +1437,8 @@ dump_file (char *p, int top_level, dev_t parent_device) block_ordinal = current_block_ordinal (); header = start_header (p, ¤t_stat_info); + if (!header) + return; header->header.typeflag = GNUTYPE_SPARSE; header_moved = 1; @@ -1371,8 +1483,8 @@ dump_file (char *p, int top_level, dev_t parent_device) sizeleft = current_stat_info.stat.st_size; - /* Don't bother opening empty, world readable files. Also do not open - files when archive is meant for /dev/null. */ + /* Don't bother opening empty, world readable files. Also do not + open files when archive is meant for /dev/null. */ if (dev_null_output || (sizeleft == 0 @@ -1398,14 +1510,21 @@ dump_file (char *p, int top_level, dev_t parent_device) { block_ordinal = current_block_ordinal (); header = start_header (p, ¤t_stat_info); + if (!header) + return; } /* Mark contiguous files, if we support them. */ - if (archive_format != V7_FORMAT && S_ISCTG (current_stat_info.stat.st_mode)) + if (archive_format != V7_FORMAT + && S_ISCTG (current_stat_info.stat.st_mode)) header->header.typeflag = CONTTYPE; - isextended = header->oldgnu_header.isextended; + if (archive_format == GNU_FORMAT || archive_format == OLDGNU_FORMAT) + isextended = header->oldgnu_header.isextended; + else + isextended = 0; + save_typeflag = header->header.typeflag; finish_header (header, block_ordinal); if (isextended) @@ -1487,7 +1606,9 @@ dump_file (char *p, int top_level, dev_t parent_device) char buf[UINTMAX_STRSIZE_BOUND]; memset (start->buffer + count, 0, bufsize - count); WARN ((0, 0, - _("%s: File shrank by %s bytes; padding with zeros"), + ngettext ("%s: File shrank by %s byte; padding with zeros", + "%s: File shrank by %s bytes; padding with zeros", + sizeleft), quotearg_colon (p), STRINGIFY_BIGINT (sizeleft, buf))); if (! ignore_failed_read_option) @@ -1572,15 +1693,16 @@ dump_file (char *p, int top_level, dev_t parent_device) return; } buffer[size] = '\0'; - if (size >= NAME_FIELD_SIZE) - write_long (buffer, GNUTYPE_LONGLINK); assign_string (¤t_stat_info.link_name, buffer); + if (size > NAME_FIELD_SIZE) + write_long_link (¤t_stat_info); block_ordinal = current_block_ordinal (); current_stat_info.stat.st_size = 0; /* force 0 size on symlink */ header = start_header (p, ¤t_stat_info); - strncpy (header->header.linkname, buffer, NAME_FIELD_SIZE); - header->header.linkname[NAME_FIELD_SIZE - 1] = '\0'; + if (!header) + return; + tar_copy_str (header->header.linkname, buffer, NAME_FIELD_SIZE); header->header.typeflag = SYMTYPE; finish_header (header, block_ordinal); /* nothing more to do to it */ @@ -1619,6 +1741,8 @@ dump_file (char *p, int top_level, dev_t parent_device) block_ordinal = current_block_ordinal (); current_stat_info.stat.st_size = 0; /* force 0 size */ header = start_header (p, ¤t_stat_info); + if (!header) + return; header->header.typeflag = type; if (type != FIFOTYPE)