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