1 /*
2  * TAR file functions for the ESP Package Manager (EPM).
3  *
4  * Copyright 1999-2019 by Michael R Sweet
5  * Copyright 1999-2010 by Easy Software Products.
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2, or (at your option)
10  * any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  */
17 
18 /*
19  * Include necessary headers...
20  */
21 
22 #include "epm.h"
23 
24 
25 /*
26  * Local globals...
27  */
28 
29 static char     last_pathname[1024] = "";
30 
31 
32 /*
33  * 'tar_close()' - Close a tar file, padding as needed.
34  */
35 
36 int					/* O - -1 on error, 0 on success */
tar_close(tarf_t * fp)37 tar_close(tarf_t *fp)			/* I - File to write to */
38 {
39   size_t	blocks;			/* Number of blocks to write */
40   int		status;			/* Return status */
41   char		padding[TAR_BLOCKS * TAR_BLOCK];
42 					/* Padding for tar blocks */
43 
44 
45   if (fp->blocks > 0)
46   {
47    /*
48     * Zero the padding record...
49     */
50 
51     memset(padding, 0, sizeof(padding));
52 
53    /*
54     * Write a single 0 block to signal the end of the archive...
55     */
56 
57     if (fwrite(padding, TAR_BLOCK, 1, fp->file) < 1)
58       return (-1);
59 
60     fp->blocks ++;
61 
62    /*
63     * Pad the tar files to TAR_BLOCKS blocks...  This is bullshit of course,
64     * but old versions of tar need it...
65     */
66 
67     status = 0;
68 
69     if ((blocks = fp->blocks % TAR_BLOCKS) > 0)
70     {
71       blocks = TAR_BLOCKS - blocks;
72 
73       if (fwrite(padding, TAR_BLOCK, blocks, fp->file) < blocks)
74 	status = -1;
75     }
76     else
77     {
78      /*
79       * Sun tar needs at least 2 0 blocks...
80       */
81 
82       if (fwrite(padding, TAR_BLOCK, blocks, fp->file) < blocks)
83 	status = -1;
84     }
85   }
86   else
87     status = 0;
88 
89  /*
90   * Close the file and free memory...
91   */
92 
93   if (fp->compressed)
94   {
95     if (pclose(fp->file))
96       status = -1;
97   }
98   else if (fclose(fp->file))
99     status = -1;
100 
101   free(fp);
102 
103   return (status);
104 }
105 
106 
107 /*
108  * 'tar_directory()' - Archive a directory.
109  */
110 
111 int					/* O - 0 on success, -1 on error */
tar_directory(tarf_t * tar,const char * srcpath,const char * dstpath)112 tar_directory(tarf_t     *tar,		/* I - Tar file to write to */
113               const char *srcpath,	/* I - Source directory */
114               const char *dstpath)	/* I - Destination directory */
115 {
116   DIR		*dir;			/* Directory */
117   DIRENT	*dent;			/* Directory entry */
118   char		src[1024],		/* Source file */
119 		dst[1024],		/* Destination file */
120 		srclink[1024];		/* Symlink value */
121   struct stat	srcinfo;		/* Information on the source file */
122 
123 
124  /*
125   * Range check input...
126   */
127 
128   if (tar == NULL || srcpath == NULL || dstpath == NULL)
129     return (-1);
130 
131  /*
132   * Try opening the source directory...
133   */
134 
135   if ((dir = opendir(srcpath)) == NULL)
136   {
137     fprintf(stderr, "epm: Unable to open directory \"%s\": %s\n", srcpath,
138             strerror(errno));
139 
140     return (-1);
141   }
142 
143  /*
144   * Read from the directory...
145   */
146 
147   while ((dent = readdir(dir)) != NULL)
148   {
149    /*
150     * Skip "." and ".."...
151     */
152 
153     if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))
154       continue;
155 
156    /*
157     * Get source file info...
158     */
159 
160     snprintf(src, sizeof(src), "%s/%s", srcpath, dent->d_name);
161     if (dstpath[0])
162       snprintf(dst, sizeof(dst), "%s/%s", dstpath, dent->d_name);
163     else
164       strlcpy(dst, dent->d_name, sizeof(dst));
165 
166     if (stat(src, &srcinfo))
167     {
168       fprintf(stderr, "epm: Unable to stat \"%s\": %s\n", src,
169               strerror(errno));
170       continue;
171     }
172 
173    /*
174     * Process accordingly...
175     */
176 
177     if (Verbosity)
178       puts(dst);
179 
180     if (S_ISDIR(srcinfo.st_mode))
181     {
182      /*
183       * Directory...
184       */
185 
186       if (tar_header(tar, TAR_DIR, srcinfo.st_mode, 0,
187                      srcinfo.st_mtime, "root", "sys", dst, NULL))
188         goto fail;
189 
190       if (tar_directory(tar, src, dst))
191         goto fail;
192     }
193     else if (S_ISLNK(srcinfo.st_mode))
194     {
195      /*
196       * Symlink...
197       */
198 
199       if (readlink(src, srclink, sizeof(srclink)) < 0)
200         goto fail;
201 
202       if (tar_header(tar, TAR_SYMLINK, srcinfo.st_mode, 0,
203                      srcinfo.st_mtime, "root", "sys", dst, srclink))
204         goto fail;
205     }
206     else
207     {
208      /*
209       * Regular file...
210       */
211 
212       if (tar_header(tar, TAR_NORMAL, srcinfo.st_mode, srcinfo.st_size,
213                      srcinfo.st_mtime, "root", "sys", dst, NULL))
214         goto fail;
215 
216       if (tar_file(tar, src))
217         goto fail;
218     }
219   }
220 
221   closedir(dir);
222   return (0);
223 
224   fail:
225 
226   closedir(dir);
227   return (-1);
228 }
229 
230 
231 /*
232  * 'tar_file()' - Write the contents of a file...
233  */
234 
235 int					/* O - 0 on success, -1 on error */
tar_file(tarf_t * fp,const char * filename)236 tar_file(tarf_t     *fp,		/* I - Tar file to write to */
237          const char *filename)		/* I - File to write */
238 {
239   FILE		*file;			/* File to write */
240   size_t	nbytes,			/* Number of bytes read */
241 		tbytes,			/* Total bytes read/written */
242 		fill;			/* Number of fill bytes needed */
243   char		buffer[8192];		/* Copy buffer */
244 
245 
246  /*
247   * Try opening the file...
248   */
249 
250   if ((file = fopen(filename, "rb")) == NULL)
251   {
252     fprintf(stderr, "epm: Unable to open \"%s\": %s\n", filename, strerror(errno));
253     return (-1);
254   }
255 
256  /*
257   * Copy the file to the tar file...
258   */
259 
260   tbytes = 0;
261 
262   while ((nbytes = fread(buffer, 1, sizeof(buffer), file)) > 0)
263   {
264    /*
265     * Zero fill the file to a 512 byte record as needed.
266     */
267 
268     if (nbytes < sizeof(buffer))
269     {
270       fill = TAR_BLOCK - (nbytes & (TAR_BLOCK - 1));
271 
272       if (fill < TAR_BLOCK)
273       {
274         memset(buffer + nbytes, 0, fill);
275         nbytes += fill;
276       }
277     }
278 
279    /*
280     * Write the buffer to the file...
281     */
282 
283     if (fwrite(buffer, 1, nbytes, fp->file) < nbytes)
284     {
285       fprintf(stderr, "epm: Unable to write file data for \"%s\": %s\n", last_pathname, strerror(errno));
286       fclose(file);
287       return (-1);
288     }
289 
290     tbytes     += nbytes;
291     fp->blocks += nbytes / TAR_BLOCK;
292   }
293 
294  /*
295   * Close the file and return...
296   */
297 
298   fclose(file);
299   return (0);
300 }
301 
302 
303 /*
304  * 'tar_header()' - Write a TAR header for the specified file...
305  */
306 
307 int					/* O - 0 on success, -1 on error */
tar_header(tarf_t * fp,int type,mode_t mode,off_t size,time_t mtime,const char * user,const char * group,const char * pathname,const char * linkname)308 tar_header(tarf_t     *fp,		/* I - Tar file to write to */
309            int        type,		/* I - File type */
310 	   mode_t     mode,		/* I - File permissions */
311 	   off_t      size,		/* I - File size */
312            time_t     mtime,		/* I - File modification time */
313 	   const char *user,		/* I - File owner */
314 	   const char *group,		/* I - File group */
315 	   const char *pathname,	/* I - File name */
316 	   const char *linkname)	/* I - File link name (for links only) */
317 {
318   tar_t		record;			/* TAR header record */
319   size_t	pathlen;		/* Length of pathname */
320   const char	*pathsep;		/* Path separator */
321   int		i,			/* Looping var... */
322 		sum;			/* Checksum */
323   unsigned char	*sumptr;		/* Pointer into header record */
324   struct passwd	*pwd;			/* Pointer to user record */
325   struct group	*grp;			/* Pointer to group record */
326 
327 
328  /*
329   * Find the username and groupname IDs...
330   */
331 
332   pwd = getpwnam(user);
333   grp = getgrnam(group);
334 
335   endpwent();
336   endgrent();
337 
338  /*
339   * Format the header...
340   */
341 
342   memset(&record, 0, sizeof(record));
343 
344   pathlen = strlen(pathname);
345 
346   if ((pathlen < (sizeof(record.header.pathname) - 1) && type != TAR_DIR) ||
347       (pathlen < (sizeof(record.header.pathname) - 2) && type == TAR_DIR))
348   {
349    /*
350     * Pathname is short enough to fit in the initial pathname field...
351     */
352 
353     strlcpy(record.header.pathname, pathname, sizeof(record.header.pathname));
354     if (type == TAR_DIR && pathname[pathlen - 1] != '/')
355       record.header.pathname[pathlen] = '/';
356   }
357   else
358   {
359    /*
360     * Copy directory information to prefix buffer and filename information
361     * to pathname buffer.
362     */
363 
364     if ((pathsep = strrchr(pathname, '/')) == NULL)
365     {
366      /*
367       * No directory info...
368       */
369 
370       fprintf(stderr, "epm: Pathname \"%s\" is too long for a tar file!\n",
371               pathname);
372       return (-1);
373     }
374 
375     if ((pathsep - pathname) > (sizeof(record.header.prefix) - 1))
376     {
377      /*
378       * Directory name too long...
379       */
380 
381       fprintf(stderr, "epm: Pathname \"%s\" is too long for a tar file!\n",
382               pathname);
383       return (-1);
384     }
385 
386     if ((pathlen - (pathsep - pathname)) > (sizeof(record.header.pathname) - 1))
387     {
388      /*
389       * Filename too long...
390       */
391 
392       fprintf(stderr, "epm: Pathname \"%s\" is too long for a tar file!\n",
393               pathname);
394       return (-1);
395     }
396 
397     strlcpy(record.header.pathname, pathsep + 1, sizeof(record.header.pathname));
398     if (type == TAR_DIR && pathname[pathlen - 1] != '/')
399       record.header.pathname[pathlen - (pathsep - pathname + 1)] = '/';
400 
401     strlcpy(record.header.prefix, pathname, (size_t)(pathsep - pathname + 1));
402   }
403 
404   snprintf(record.header.mode, sizeof(record.header.mode), "%-6o ", (unsigned)mode);
405   snprintf(record.header.uid, sizeof(record.header.uid), "%o ", pwd == NULL ? 0 : (unsigned)pwd->pw_uid);
406   snprintf(record.header.gid, sizeof(record.header.gid), "%o ", grp == NULL ? 0 : (unsigned)grp->gr_gid);
407   snprintf(record.header.size, sizeof(record.header.size), "%011o", (unsigned)size);
408   snprintf(record.header.mtime, sizeof(record.header.mtime), "%011o", (unsigned)mtime);
409   memset(&(record.header.chksum), ' ', sizeof(record.header.chksum));
410   record.header.linkflag = type;
411   if (type == TAR_SYMLINK)
412     strlcpy(record.header.linkname, linkname, sizeof(record.header.linkname));
413   strlcpy(record.header.magic, TAR_MAGIC, sizeof(record.header.magic));
414   memcpy(record.header.version, TAR_VERSION, 2);
415   strlcpy(record.header.uname, user, sizeof(record.header.uname));
416   strlcpy(record.header.gname, group, sizeof(record.header.uname));
417 
418  /*
419   * Compute the checksum of the header...
420   */
421 
422   for (i = sizeof(record), sumptr = record.all, sum = 0; i > 0; i --)
423     sum += *sumptr++;
424 
425  /*
426   * Put the correct checksum in place and write the header...
427   */
428 
429   snprintf(record.header.chksum, sizeof(record.header.chksum), "%6o", sum);
430 
431   if (fwrite(&record, 1, sizeof(record), fp->file) < sizeof(record))
432   {
433     static const char * const types[] =
434     {
435       "file",
436       "link",
437       "symbolic link",
438       "character file",
439       "block file",
440       "directory",
441       "named pipe",
442       "contiguous file"
443     };
444 
445     fprintf(stderr, "epm: Error writing %s header for \"%s\": %s\n", types[type - '0'], pathname, strerror(errno));
446     return (-1);
447   }
448 
449   strlcpy(last_pathname, pathname, sizeof(last_pathname));
450 
451   fp->blocks ++;
452   return (0);
453 }
454 
455 
456 /*
457  * 'tar_open()' - Open a TAR file for writing.
458  */
459 
460 tarf_t *				/* O - New tar file */
tar_open(const char * filename,int compress)461 tar_open(const char *filename,		/* I - File to create */
462          int        compress)		/* I - Compress with gzip? */
463 {
464   tarf_t	*fp;			/* New tar file */
465   char		command[1024];		/* Compression command */
466 
467 
468  /*
469   * Allocate memory for the tar file state...
470   */
471 
472   if ((fp = calloc(sizeof(tarf_t), 1)) == NULL)
473     return (NULL);
474 
475  /*
476   * Open the output file or pipe into gzip for compressed output...
477   */
478 
479   if (compress)
480   {
481     snprintf(command, sizeof(command), EPM_GZIP " > %s", filename);
482     fp->file = popen(command, "w");
483   }
484   else
485     fp->file = fopen(filename, "wb");
486 
487  /*
488   * If we couldn't open the output file, abort...
489   */
490 
491   if (fp->file == NULL)
492   {
493     free(fp);
494     return (NULL);
495   }
496 
497  /*
498   * Save the compression state and return...
499   */
500 
501   fp->compressed = compress;
502 
503   return (fp);
504 }
505 
506 
507 /*
508  * 'tar_package()' - Archive a package file.
509  */
510 
511 int					/* O - 0 on success, -1 on failure */
tar_package(tarf_t * tar,const char * ext,const char * prodname,const char * directory,const char * platname,dist_t * dist,const char * subpackage)512 tar_package(tarf_t     *tar,		/* I - Tar file */
513             const char *ext,		/* I - Package filename extension */
514             const char *prodname,	/* I - Product short name */
515 	    const char *directory,	/* I - Directory for distribution files */
516             const char *platname,	/* I - Platform name */
517             dist_t     *dist,		/* I - Distribution information */
518 	    const char *subpackage)	/* I - Subpackage */
519 {
520   char		prodfull[255],		/* Full name of product */
521 		name[1024],		/* Full product name */
522 		filename[1024];		/* File to archive */
523   struct stat	filestat;		/* File information */
524 
525 
526  /*
527   * Figure out the full name of the distribution...
528   */
529 
530   if (subpackage)
531     snprintf(prodfull, sizeof(prodfull), "%s-%s", prodname, subpackage);
532   else
533     strlcpy(prodfull, prodname, sizeof(prodfull));
534 
535  /*
536   * Then the subdirectory name...
537   */
538 
539   if (dist->release[0])
540     snprintf(name, sizeof(name), "%s-%s-%s", prodfull, dist->version,
541              dist->release);
542   else
543     snprintf(name, sizeof(name), "%s-%s", prodfull, dist->version);
544 
545   if (platname[0])
546   {
547     strlcat(name, "-", sizeof(name));
548     strlcat(name, platname, sizeof(name));
549   }
550 
551   strlcat(name, ".", sizeof(name));
552   strlcat(name, ext, sizeof(name));
553 
554  /*
555   * Find out more about the package file...
556   */
557 
558   snprintf(filename, sizeof(filename), "%s/%s", directory, name);
559   if (stat(filename, &filestat))
560   {
561     fprintf(stderr, "epm: Error reading package file \"%s\": %s\n", filename, strerror(errno));
562     return (-1);
563   }
564 
565  /*
566   * Write the header and the file...
567   */
568 
569   if (tar_header(tar, TAR_NORMAL, (mode_t)0644, filestat.st_size,
570                  filestat.st_mtime, "root", "root", name, NULL))
571     return (-1);
572 
573   return (tar_file(tar, filename));
574 }
575