[PATCH] tar: remove leading / and ../ on reading and writing

Alexander Shishkin virtuoso at slind.org
Mon Feb 28 17:12:30 UTC 2011


On Mon, Feb 28, 2011 at 05:57:37PM +0100, walter harms wrote:
> 

Thanks for your comments, I'll see if I can come up with something smaller.

> 
> Am 28.02.2011 11:24, schrieb Alexander Shishkin:
> > Currently, tar will add members with names starting with
> > the prefixes to an archive unmodified, and will then refuse
> > to extract from such archive. However, GNU tar will strip
> > these prefixes upon creating the archive and reading from
> > it.
> > 
> > function                                             old     new   delta
> > strip_leading_junk                                     -     130    +130
> > static.pfx                                             -      32     +32
> > run_applet_and_exit                                  811     810      -1
> > .rodata                                           147143  147142      -1
> > writeFileToTarball                                   535     500     -35
> > get_header_tar                                      1642    1598     -44
> > ------------------------------------------------------------------------------
> > (add/remove: 2/0 grow/shrink: 0/4 up/down: 162/-81)            Total: 81 bytes
> > 
> > Signed-off-by: Alexander Shishkin <virtuoso at slind.org>
> > ---
> >  archival/libarchive/get_header_tar.c |   48 +++++++++++++++++++++++++++------
> >  archival/tar.c                       |   11 +-------
> >  include/archive.h                    |    2 +
> >  3 files changed, 42 insertions(+), 19 deletions(-)
> > 
> > diff --git a/archival/libarchive/get_header_tar.c b/archival/libarchive/get_header_tar.c
> > index 2e03327..d4e904c 100644
> > --- a/archival/libarchive/get_header_tar.c
> > +++ b/archival/libarchive/get_header_tar.c
> > @@ -118,11 +118,44 @@ static char *get_selinux_sctx_from_pax_hdr(archive_handle_t *archive_handle, uns
> >  }
> >  #endif
> >  
> > +FAST_FUNC const char *strip_leading_junk(const char *file_name)
> > +{
> > +	static struct {
> > +		const char *prefix;
> > +		int len;
> > +	} pfx[] = {
> > +		{"/", 1},
> > +		{"../", 3}
> > +	};
> > +
> > +	while (*file_name) {
> > +		static smallint warned;
> > +		int i;
> > +
> > +		for (i = 0; i < ARRAY_SIZE(pfx); i++) {
> > +			if (!strncmp(file_name, pfx[i].prefix, pfx[i].len)) {
> > +				file_name += pfx[i].len;
> > +				break;
> > +			}
> > +		}
> 
> 
> do you expect much more ? maybe 2 * strcmp() is smaller ?

Not if you move the warning inside.

> 
> 
> > +		if (i == ARRAY_SIZE(pfx))
> > +			break;
> 
> 	perhaps you can move the warning into the if strcmp() and make a function from this loop ?

Not sure if there's any value in that.

> 	btw: you are warning only once, ok but the pattern changes from "/" to "../" i will not see
> 	the warning again. is that intended ?

GNU tar will print a warning once per prefix. I could add another field
to the structure to track that instead, but I'm not sure if there's much
value in that warning too.

> 
> 
> > +		if (!warned) {
> > +			bb_error_msg("removing leading '%s' from member names",
> > +				     pfx[i].prefix);
> > +			warned = 1;
> > +		}
> > +	}
> > +
> > +	return file_name;
> > +}
> > +
> >  char FAST_FUNC get_header_tar(archive_handle_t *archive_handle)
> >  {
> >  	file_header_t *file_header = archive_handle->file_header;
> >  	struct tar_header_t tar;
> > -	char *cp;
> > +	char *cp, *real_name;
> >  	int i, sum_u, sum;
> >  #if ENABLE_FEATURE_TAR_OLDSUN_COMPATIBILITY
> >  	int sum_s;
> > @@ -422,12 +455,9 @@ char FAST_FUNC get_header_tar(archive_handle_t *archive_handle)
> >  		p_linkname = NULL;
> >  	}
> >  #endif
> > -	if (strncmp(file_header->name, "/../"+1, 3) == 0
> > -	 || strstr(file_header->name, "/../")
> > -	) {
> > -		bb_error_msg_and_die("name with '..' encountered: '%s'",
> > -				file_header->name);
> > -	}
> > +
> > +	real_name = file_header->name;
> > +	file_header->name = (char *)strip_leading_junk(real_name);
> 
> i guess this (char *) is caused by the cons char * of the function ? may be just removing const is simpler.

Technically, it's the same string. And writeFileToTarball() will call it with
a const char * parameter.

> 
> 
> >  	/* Strip trailing '/' in directories */
> >  	/* Must be done after mode is set as '/' is used to check if it's a directory */
> > @@ -443,10 +473,10 @@ char FAST_FUNC get_header_tar(archive_handle_t *archive_handle)
> >  		if (archive_handle->accept || archive_handle->reject)
> >  			llist_add_to(&archive_handle->passed, file_header->name);
> >  		else /* Caller isn't interested in list of unpacked files */
> > -			free(file_header->name);
> > +			free(real_name);
> >  	} else {
> >  		data_skip(archive_handle);
> > -		free(file_header->name);
> > +		free(real_name);
> >  	}
> >  	archive_handle->offset += file_header->size;
> >  
> > diff --git a/archival/tar.c b/archival/tar.c
> > index 1e3cecf..4f5087e 100644
> > --- a/archival/tar.c
> > +++ b/archival/tar.c
> > @@ -398,16 +398,7 @@ static int FAST_FUNC writeFileToTarball(const char *fileName, struct stat *statb
> >  	DBG("writeFileToTarball('%s')", fileName);
> >  
> >  	/* Strip leading '/' (must be before memorizing hardlink's name) */
> > -	header_name = fileName;
> > -	while (header_name[0] == '/') {
> > -		static smallint warned;
> > -
> > -		if (!warned) {
> > -			bb_error_msg("removing leading '/' from member names");
> > -			warned = 1;
> > -		}
> > -		header_name++;
> > -	}
> > +	header_name = strip_leading_junk(fileName);
> >  
> >  	if (header_name[0] == '\0')
> >  		return TRUE;
> > diff --git a/include/archive.h b/include/archive.h
> > index 49c4787..e619c0e 100644
> > --- a/include/archive.h
> > +++ b/include/archive.h
> > @@ -224,6 +224,8 @@ int bbunpack(char **argv,
> >  	    const char *expected_ext
> >  ) FAST_FUNC;
> >  
> > +const char *strip_leading_junk(const char *file_name) FAST_FUNC;
> > +
> >  #if BB_MMU
> >  void open_transformer(int fd,
> >  	IF_DESKTOP(long long) int FAST_FUNC (*transformer)(int src_fd, int dst_fd)) FAST_FUNC;


More information about the busybox mailing list