Date: Fri, 28 Mar 2003 22:47:43 -0800 From: Tim Kientzle <kientzle@acm.org> To: "Bruce A. Mah" <bmah@FreeBSD.ORG> Cc: freebsd-hackers@FreeBSD.ORG Subject: Re: Making pkg_XXX tools smarter about file types... Message-ID: <3E85418F.8010201@acm.org> References: <3E42C148.4050807@acm.org> <20030329012828.GA32891@intruder.bmah.org>
next in thread | previous in thread | raw e-mail | index | archive | help
This is a multi-part message in MIME format. --------------070001080502070007020104 Content-Type: text/plain; charset=us-ascii; format=flowed Content-Transfer-Encoding: 7bit Bruce A. Mah wrote: > If memory serves me right, Tim Kientzle wrote: > >>The attached patch modifies the pkg_install >>tools to inspect the file contents--rather than the >>filename extension--to determine the >>compression method in use. > > The concept is good, and it's something we've needed for awhile. I > suspect you followed the various adventures of pkg_add and sysinstall > when we tried supporting both bzip2 and gzip packages ... Yes, those adventures were a large part of what motivated me to implement this auto-detect logic. > ... If the > package comes from a file, then pkg_add wants to make two passes over > the package, first to get the +CONTENTS file and second to actually > unpack everything. When the first tar process finishes reading the > +CONTENTS file, it closes its pipe (due to the --fast-read argument). > However, pkg_add still seems to be writing to the pipe...this seems to > be bad. > > It works if I remove the --fast-read flag from the tar, but that's not > the right answer. No. That's clearly not the right answer. Seems I forgot to check for an error return from fwrite(). Easy enough to fix; near the end of unpack() in lib/file.c, change the read/write loop to the following: while(buff_size > 0) { if(buff_size < fwrite(buff,1,buff_size,out_pipe)) break; buff_size = fread(buff,1,buff_allocation,pkg_file); } This aborts the passthrough if the pipe is closed. Modified diff attached. Tim P.S. It's galled me for a while that pkg_add has to fork 'tar' to extract the archive. I've started piecing together a library that reads/writes tarfiles. With this, it should be possible to make pkg_add considerably more efficient. In particular, rather than extracting to a temp directory, then parsing important information, then moving the files, it should be possible using this library to read the initial entries ("+CONTENTS", in particular) directly into memory, process the information there, then extract the remainder of the package files directly into their final locations. So far, I have a library API outlined, and functional read support implemented. Next step is to hack up a minimal tar implementation that uses it to make sure everything's working correctly. So far, the library automatically detects compression formats (using techniques like those in my pkg_install patch) and has some rough support for detecting the archive format as well. (One goal of mine: support for 'pax extended archives', which I understand can handle ACLs.) Of course, such a library could also form the basis for a BSD-licensed tar to replace GNU tar. I understand a few people have wanted such a thing. --------------070001080502070007020104 Content-Type: text/plain; name="kientzle_pkg_install-2.diff" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="kientzle_pkg_install-2.diff" Index: lib/exec.c =================================================================== RCS file: /usr/src/cvs/src/usr.sbin/pkg_install/lib/exec.c,v retrieving revision 1.10 diff -c -r1.10 exec.c *** lib/exec.c 1 Apr 2002 09:39:07 -0000 1.10 --- lib/exec.c 29 Mar 2003 06:00:36 -0000 *************** *** 25,59 **** #include <err.h> /* ! * Unusual system() substitute. Accepts format string and args, ! * builds and executes command. Returns exit code. */ ! ! int ! vsystem(const char *fmt, ...) { ! va_list args; char *cmd; - int ret, maxargs; maxargs = sysconf(_SC_ARG_MAX); maxargs -= 32; /* some slop for the sh -c */ cmd = malloc(maxargs); if (!cmd) { ! warnx("vsystem can't alloc arg space"); ! return 1; } - - va_start(args, fmt); if (vsnprintf(cmd, maxargs, fmt, args) > maxargs) { ! warnx("vsystem args are too long"); ! return 1; } #ifdef DEBUG printf("Executing %s\n", cmd); #endif ret = system(cmd); - va_end(args); free(cmd); return ret; } --- 25,83 ---- #include <err.h> /* ! * Format a command, allocating a buffer along the way. */ ! static char * ! va_system_cmd(const char *fmt, va_list args) { ! int maxargs; char *cmd; maxargs = sysconf(_SC_ARG_MAX); maxargs -= 32; /* some slop for the sh -c */ cmd = malloc(maxargs); if (!cmd) { ! warnx("can't allocate memory to format program command line"); ! return NULL; } if (vsnprintf(cmd, maxargs, fmt, args) > maxargs) { ! warnx("argument list is too long"); ! return NULL; } + return cmd; + } + + char * + system_cmd(const char *fmt, ...) + { + va_list args; + char *cmd; + + va_start(args, fmt); + cmd = va_system_cmd(fmt,args); + va_end(args); + return cmd; + } + + /* + * Unusual system() substitute. Accepts format string and args, + * builds and executes command. Returns exit code. + */ + int + vsystem(const char *fmt, ...) + { + va_list args; + char *cmd; + int ret; + + va_start(args, fmt); + cmd = va_system_cmd(fmt,args); + va_end(args); + if(cmd == NULL) return 1; #ifdef DEBUG printf("Executing %s\n", cmd); #endif ret = system(cmd); free(cmd); return ret; } *************** *** 63,69 **** { FILE *fp; char *cmd, *rp; - int maxargs; va_list args; rp = malloc(MAXPATHLEN); --- 87,92 ---- *************** *** 71,94 **** warnx("vpipe can't alloc buffer space"); return NULL; } ! maxargs = sysconf(_SC_ARG_MAX); ! maxargs -= 32; /* some slop for the sh -c */ ! cmd = alloca(maxargs); ! if (!cmd) { ! warnx("vpipe can't alloc arg space"); ! return NULL; ! } - va_start(args, fmt); - if (vsnprintf(cmd, maxargs, fmt, args) > maxargs) { - warnx("vsystem args are too long"); - return NULL; - } #ifdef DEBUG fprintf(stderr, "Executing %s\n", cmd); #endif fflush(NULL); fp = popen(cmd, "r"); if (fp == NULL) { warnx("popen() failed"); return NULL; --- 94,110 ---- warnx("vpipe can't alloc buffer space"); return NULL; } ! va_start(args,fmt); ! cmd = va_system_cmd(fmt,args); ! va_end(args); ! if(cmd == NULL) return NULL; #ifdef DEBUG fprintf(stderr, "Executing %s\n", cmd); #endif fflush(NULL); fp = popen(cmd, "r"); + free(cmd); if (fp == NULL) { warnx("popen() failed"); return NULL; *************** *** 97,103 **** #ifdef DEBUG fprintf(stderr, "Returned %s\n", rp); #endif - va_end(args); if (pclose(fp) || (strlen(rp) == 0)) { free(rp); return NULL; --- 113,118 ---- Index: lib/file.c =================================================================== RCS file: /usr/src/cvs/src/usr.sbin/pkg_install/lib/file.c,v retrieving revision 1.64 diff -c -r1.64 file.c *** lib/file.c 6 Jan 2003 07:39:02 -0000 1.64 --- lib/file.c 29 Mar 2003 06:12:55 -0000 *************** *** 27,32 **** --- 27,36 ---- #include <time.h> #include <sys/wait.h> + static int file_is_gzip(const unsigned char *, size_t); + static int file_is_bzip2(const unsigned char *, size_t); + + /* Quick check to see if a file exists */ Boolean fexists(const char *fname) *************** *** 42,48 **** isdir(const char *fname) { struct stat sb; ! if (lstat(fname, &sb) != FAIL && S_ISDIR(sb.st_mode)) return TRUE; else if (lstat(strconcat(fname, "/."), &sb) != FAIL && S_ISDIR(sb.st_mode)) --- 46,52 ---- isdir(const char *fname) { struct stat sb; ! if (lstat(fname, &sb) != FAIL && S_ISDIR(sb.st_mode)) return TRUE; else if (lstat(strconcat(fname, "/."), &sb) != FAIL && S_ISDIR(sb.st_mode)) *************** *** 328,362 **** int unpack(const char *pkg, const char *flist) { ! char args[10], suff[80], *cp; ! args[0] = '\0'; ! /* ! * Figure out by a crude heuristic whether this or not this is probably ! * compressed and whichever compression utility was used (gzip or bzip2). ! */ if (strcmp(pkg, "-")) { ! cp = strrchr(pkg, '.'); ! if (cp) { ! strcpy(suff, cp + 1); ! if (strchr(suff, 'z') || strchr(suff, 'Z')) { ! if (strchr(suff, 'b')) ! strcpy(args, "-j"); ! else ! strcpy(args, "-z"); ! } ! } } ! else ! /* XXX: need to handle .tgz also */ ! strcpy(args, "-j"); ! strcat(args, " -xpf"); ! if (vsystem("tar %s '%s' %s", args, pkg, flist ? flist : "")) { warnx("tar extract of %s failed!", pkg); return 1; } return 0; } /* * Using fmt, replace all instances of: --- 332,411 ---- int unpack(const char *pkg, const char *flist) { ! char *cmd; ! char *compression; ! FILE *pkg_file; ! FILE *out_pipe; ! unsigned char *buff; ! size_t buff_allocation; ! size_t buff_size; ! buff_allocation = 8*1024; ! ! buff = (unsigned char *)malloc(buff_allocation); ! /* Determine compression by inspecting file signature */ if (strcmp(pkg, "-")) { ! pkg_file = fopen(pkg,"r"); ! } else { ! pkg_file = stdin; } ! if(pkg_file == NULL) { ! warnx("couldn't open %s",pkg); ! return 1; ! } ! ! /* Select appropriate compression argument for tar by ! * inspecting file signature */ ! buff_size = fread(buff,1,buff_allocation,pkg_file); ! if(file_is_gzip(buff,buff_size)) { ! compression = "z"; ! } else if(file_is_bzip2(buff,buff_size)) { ! compression = "j"; ! } else { ! compression = ""; ! } ! ! cmd = system_cmd("tar -xp%sf - %s",compression,flist ? flist : ""); ! if(cmd == NULL) return 1; ! ! /* Cat entire file through tar */ ! #ifdef DEBUG ! printf("Piping package '%s' to cmd '%s'\n", pkg,cmd); ! #endif ! out_pipe = popen(cmd,"w"); ! free(cmd); ! ! if(out_pipe == NULL) return 1; ! while(buff_size > 0) { ! if(buff_size < fwrite(buff,1,buff_size,out_pipe)) ! break; ! buff_size = fread(buff,1,buff_allocation,pkg_file); ! } ! if(pclose(out_pipe)) { warnx("tar extract of %s failed!", pkg); return 1; } return 0; } + + /* + * Returns 1 if buffer holds initial bytes of a gzipped file + */ + static int + file_is_gzip(const unsigned char *head, size_t s) { + if(s < 2) return 0; + return (head[0]==0037 && head[1]==0213); + } + + /* + * Returns 1 if buffer holds initial bytes of a bzip2-ed file + */ + static int + file_is_bzip2(const unsigned char *head, size_t s) { + if(s < 3) return 0; + return (head[0]=='B' && head[1]=='Z' && head[2]=='h'); + } + /* * Using fmt, replace all instances of: Index: lib/lib.h =================================================================== RCS file: /usr/src/cvs/src/usr.sbin/pkg_install/lib/lib.h,v retrieving revision 1.47 diff -c -r1.47 lib.h *** lib/lib.h 6 Jan 2003 07:39:02 -0000 1.47 --- lib/lib.h 29 Mar 2003 06:00:36 -0000 *************** *** 137,142 **** --- 137,143 ---- /* Prototypes */ /* Misc */ int vsystem(const char *, ...); + char * system_cmd(const char *, ...); char *vpipe(const char *, ...); void cleanup(int); char *make_playpen(char *, off_t); --------------070001080502070007020104--
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?3E85418F.8010201>