Add unfinished gpgtar.
[gnupg.git] / tools / gpgtar-create.c
1 /* gpgtar-create.c - Create a TAR archive
2  * Copyright (C) 2010 Free Software Foundation, Inc.
3  *
4  * This file is part of GnuPG.
5  *
6  * GnuPG is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * GnuPG is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <config.h>
21 #include <errno.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <dirent.h>
28 /* #ifdef HAVE_W32_SYSTEM */
29 /* # define WIN32_LEAN_AND_MEAN */
30 /* # include <windows.h> */
31 /* #else /\*!HAVE_W32_SYSTEM*\/ */
32 # include <unistd.h>
33 # include <pwd.h>
34 # include <grp.h>
35 /* #endif /\*!HAVE_W32_SYSTEM*\/ */
36 #include <assert.h>
37
38 #include "i18n.h"
39 #include "../common/sysutils.h"
40 #include "gpgtar.h"
41
42 #ifndef HAVE_LSTAT
43 #define lstat(a,b) stat ((a), (b))
44 #endif
45
46
47 /* Object to control the file scanning.  */
48 struct scanctrl_s;
49 typedef struct scanctrl_s *scanctrl_t;
50 struct scanctrl_s
51 {
52   tar_header_t flist;
53   tar_header_t *flist_tail;
54   int nestlevel;
55 };
56
57
58
59
60 /* Given a fresh header object HDR with only the name field set, try
61    to gather all available info.  */
62 static gpg_error_t
63 fillup_entry (tar_header_t hdr)
64 {
65   gpg_error_t err;
66   struct stat sbuf;
67
68   if (lstat (hdr->name, &sbuf))
69     {
70       err = gpg_error_from_syserror ();
71       log_error ("error stat-ing `%s': %s\n", hdr->name, gpg_strerror (err));
72       return err;
73     }
74   
75   if (S_ISREG (sbuf.st_mode))
76     hdr->typeflag = TF_REGULAR;
77   else if (S_ISDIR (sbuf.st_mode))
78     hdr->typeflag = TF_DIRECTORY;
79   else if (S_ISCHR (sbuf.st_mode))
80     hdr->typeflag = TF_CHARDEV;
81   else if (S_ISBLK (sbuf.st_mode))
82     hdr->typeflag = TF_BLOCKDEV;
83   else if (S_ISFIFO (sbuf.st_mode))
84     hdr->typeflag = TF_FIFO;
85   else if (S_ISLNK (sbuf.st_mode))
86     hdr->typeflag = TF_SYMLINK;
87   else 
88     hdr->typeflag = TF_NOTSUP;
89
90   /* FIXME: Save DEV and INO? */
91
92   /* Set the USTAR defined mode bits using the system macros.  */
93   if (sbuf.st_mode & S_IRUSR)
94     hdr->mode |= 0400;
95   if (sbuf.st_mode & S_IWUSR)
96     hdr->mode |= 0200;
97   if (sbuf.st_mode & S_IXUSR)
98     hdr->mode |= 0100;
99   if (sbuf.st_mode & S_IRGRP)
100     hdr->mode |= 0040;
101   if (sbuf.st_mode & S_IWGRP)
102     hdr->mode |= 0020;
103   if (sbuf.st_mode & S_IXGRP)
104     hdr->mode |= 0010;
105   if (sbuf.st_mode & S_IROTH)
106     hdr->mode |= 0004;
107   if (sbuf.st_mode & S_IWOTH)
108     hdr->mode |= 0002;
109   if (sbuf.st_mode & S_IXOTH)
110     hdr->mode |= 0001;
111 #ifdef S_IXUID
112   if (sbuf.st_mode & S_IXUID)
113     hdr->mode |= 04000;
114 #endif
115 #ifdef S_IXGID
116   if (sbuf.st_mode & S_IXGID)
117     hdr->mode |= 02000;
118 #endif
119 #ifdef S_ISVTX
120   if (sbuf.st_mode & S_ISVTX)
121     hdr->mode |= 01000;
122 #endif
123
124   hdr->nlink = sbuf.st_nlink;
125
126   hdr->uid = sbuf.st_uid;
127   hdr->gid = sbuf.st_gid;
128
129   /* Only set the size for a regular file.  */
130   if (hdr->typeflag == TF_REGULAR)
131     hdr->size = sbuf.st_size;
132
133   hdr->mtime = sbuf.st_mtime;
134   
135
136   return 0;
137 }
138
139
140
141 static gpg_error_t
142 add_entry (const char *dname, size_t dnamelen, struct dirent *de,
143            scanctrl_t scanctrl)
144 {
145   gpg_error_t err;
146   tar_header_t hdr;
147   char *p;
148
149   assert (dnamelen);
150
151   hdr = xtrycalloc (1, sizeof *hdr + dnamelen + 1
152                     + (de? strlen (de->d_name) : 0));
153   if (!hdr)
154     {
155       err = gpg_error_from_syserror ();
156       log_error (_("error reading directory `%s': %s\n"),
157                  dname, gpg_strerror (err));
158       return err;
159     }
160
161   p = stpcpy (hdr->name, dname);
162   if (de)
163     {
164       if (dname[dnamelen-1] != '/')
165         *p++ = '/';
166       strcpy (p, de->d_name);
167     }
168   else
169     {
170       if (hdr->name[dnamelen-1] == '/')
171         hdr->name[dnamelen-1] = 0;
172     }
173 #ifdef HAVE_DOSISH_SYSTEM
174   for (p=hdr->name; *p; p++)
175     if (*p == '\\')
176       *p = '/';
177 #endif
178   err = fillup_entry (hdr);
179   if (err)
180     xfree (hdr);
181   else
182     {
183       if (opt.verbose)
184         gpgtar_print_header (hdr, log_get_stream ());
185       *scanctrl->flist_tail = hdr;
186       scanctrl->flist_tail = &hdr->next;
187     }
188
189   return 0;
190 }
191
192
193 static gpg_error_t
194 scan_directory (const char *dname, scanctrl_t scanctrl)
195 {
196   gpg_error_t err = 0;
197   size_t dnamelen;
198   DIR *dir;
199   struct dirent *de;
200
201   dnamelen = strlen (dname);
202   if (!dnamelen)
203     return 0;  /* An empty directory name has no entries.  */
204
205   dir = opendir (dname);
206   if (!dir)
207     {
208       err = gpg_error_from_syserror ();
209       log_error (_("error reading directory `%s': %s\n"),
210                  dname, gpg_strerror (err));
211       return err;
212     }
213   
214   while ((de = readdir (dir)))
215     {
216       if (!strcmp (de->d_name, "." ) || !strcmp (de->d_name, ".."))
217         continue; /* Skip self and parent dir entry.  */
218       
219       err = add_entry (dname, dnamelen, de, scanctrl);
220       if (err)
221         goto leave;
222      }
223
224  leave:
225   closedir (dir);
226   return err;
227 }
228
229
230 static gpg_error_t
231 scan_recursive (const char *dname, scanctrl_t scanctrl)
232 {
233   gpg_error_t err = 0;
234   tar_header_t hdr, *start_tail, *stop_tail;
235
236   if (scanctrl->nestlevel > 200)
237     {
238       log_error ("directories too deeply nested\n");
239       return gpg_error (GPG_ERR_RESOURCE_LIMIT);
240     }
241   scanctrl->nestlevel++;
242
243   assert (scanctrl->flist_tail);
244   start_tail = scanctrl->flist_tail;
245   scan_directory (dname, scanctrl);
246   stop_tail = scanctrl->flist_tail;
247   hdr = *start_tail;
248   for (; hdr && hdr != *stop_tail; hdr = hdr->next)
249     if (hdr->typeflag == TF_DIRECTORY)
250       {
251         if (opt.verbose > 1)
252           log_info ("scanning directory `%s'\n", hdr->name);
253         scan_recursive (hdr->name, scanctrl);
254       }
255   
256   scanctrl->nestlevel--;
257   return err;
258 }
259
260
261 /* Returns true if PATTERN is acceptable.  */
262 static int
263 pattern_valid_p (const char *pattern)
264 {
265   if (!*pattern)
266     return 0;
267   if (*pattern == '.' && pattern[1] == '.')
268     return 0;
269   if (*pattern == '/' || *pattern == DIRSEP_C)
270     return 0; /* Absolute filenames are not supported.  */
271 #ifdef HAVE_DRIVE_LETTERS
272   if (((*pattern >= 'a' && *pattern <= 'z')
273        || (*pattern >= 'A' && *pattern <= 'Z'))
274       && pattern[1] == ':')
275     return 0; /* Drive letter are not allowed either.  */
276 #endif /*HAVE_DRIVE_LETTERS*/ 
277
278   return 1; /* Okay.  */
279 }
280
281
282 \f
283 static void
284 store_xoctal (char *buffer, size_t length, unsigned long long value)
285 {
286   char *p, *pend;
287   size_t n;
288   unsigned long long v;
289
290   assert (length > 1);
291
292   v = value;
293   n = length;
294   p = pend = buffer + length;
295   *--p = 0; /* Nul byte.  */
296   n--;
297   do
298     {
299       *--p = '0' + (v % 8);
300       v /= 8;
301       n--;
302     }
303   while (v && n);
304   if (!v)
305     {
306       /* Pad.  */
307       for ( ; n; n--)
308         *--p = '0';
309     }
310   else /* Does not fit into the field.  Store as binary number.  */
311     {
312       v = value;
313       n = length;
314       p = pend = buffer + length;
315       do
316         {
317           *--p = v;
318           v /= 256;
319           n--;
320         }
321       while (v && n);
322       if (!v)
323         {
324           /* Pad.  */
325           for ( ; n; n--)
326             *--p = 0;
327           if (*p & 0x80)
328             BUG ();
329           *p |= 0x80; /* Set binary flag.  */
330         }
331       else
332         BUG ();
333     }
334 }
335
336
337 static void
338 store_uname (char *buffer, size_t length, unsigned long uid)
339 {
340   static int initialized;
341   static unsigned long lastuid;
342   static char lastuname[32];
343
344   if (!initialized || uid != lastuid)
345     {
346       struct passwd *pw = getpwuid (uid);
347
348       lastuid = uid;
349       initialized = 1;
350       if (pw)
351         mem2str (lastuname, pw->pw_name, sizeof lastuname); 
352       else
353         {
354           log_info ("failed to get name for uid %lu\n", uid);
355           *lastuname = 0;
356         }
357     }
358   mem2str (buffer, lastuname, length);
359 }
360
361
362 static void
363 store_gname (char *buffer, size_t length, unsigned long gid)
364 {
365   static int initialized;
366   static unsigned long lastgid;
367   static char lastgname[32];
368
369   if (!initialized || gid != lastgid)
370     {
371       struct group *gr = getgrgid (gid);
372
373       lastgid = gid;
374       initialized = 1;
375       if (gr)
376         mem2str (lastgname, gr->gr_name, sizeof lastgname); 
377       else
378         {
379           log_info ("failed to get name for gid %lu\n", gid);
380           *lastgname = 0;
381         }
382     }
383   mem2str (buffer, lastgname, length);
384 }
385
386
387 static gpg_error_t
388 build_header (void *record, tar_header_t hdr)
389 {
390   gpg_error_t err;
391   struct ustar_raw_header *raw = record;
392   size_t namelen, n;
393   unsigned long chksum;
394   unsigned char *p;
395
396   memset (record, 0, RECORDSIZE);
397
398   /* Store name and prefix.  */
399   namelen = strlen (hdr->name);
400   if (namelen < sizeof raw->name)
401     memcpy (raw->name, hdr->name, namelen);
402   else 
403     {
404       n = (namelen < sizeof raw->prefix)? namelen : sizeof raw->prefix;
405       for (n--; n ; n--)
406         if (hdr->name[n] == '/')
407           break;
408       if (namelen - n < sizeof raw->name)
409         {
410           /* Note that the N is < sizeof prefix and that the
411              delimiting slash is not stored.  */
412           memcpy (raw->prefix, hdr->name, n);
413           memcpy (raw->name, hdr->name+n+1, namelen - n);
414         }
415       else
416         {
417           err = gpg_error (GPG_ERR_TOO_LARGE);
418           log_error ("error storing file `%s': %s\n", 
419                      hdr->name, gpg_strerror (err));
420           return err;
421         }
422     }
423   
424   store_xoctal (raw->mode,  sizeof raw->mode,  hdr->mode);
425   store_xoctal (raw->uid,   sizeof raw->uid,   hdr->uid);
426   store_xoctal (raw->gid,   sizeof raw->gid,   hdr->gid);
427   store_xoctal (raw->size,  sizeof raw->size,  hdr->size);
428   store_xoctal (raw->mtime, sizeof raw->mtime, hdr->mtime);
429
430   switch (hdr->typeflag)
431     {
432     case TF_REGULAR:   raw->typeflag[0] = '0'; break;
433     case TF_HARDLINK:  raw->typeflag[0] = '1'; break;
434     case TF_SYMLINK:   raw->typeflag[0] = '2'; break;
435     case TF_CHARDEV:   raw->typeflag[0] = '3'; break;
436     case TF_BLOCKDEV:  raw->typeflag[0] = '4'; break;
437     case TF_DIRECTORY: raw->typeflag[0] = '5'; break;
438     case TF_FIFO:      raw->typeflag[0] = '6'; break;
439     default: return gpg_error (GPG_ERR_NOT_SUPPORTED);
440     }
441
442   memcpy (raw->magic, "ustar", 6);
443   raw->version[0] = '0';
444   raw->version[1] = '0';
445
446   store_uname (raw->uname, sizeof raw->uname, hdr->uid);
447   store_gname (raw->gname, sizeof raw->gname, hdr->gid);
448
449   if (hdr->typeflag == TF_SYMLINK)
450     {
451       int nread;
452
453       nread = readlink (hdr->name, raw->linkname, sizeof raw->linkname -1);
454       if (nread < 0)
455         {
456           err = gpg_error_from_syserror ();
457           log_error ("error reading symlink `%s': %s\n", 
458                      hdr->name, gpg_strerror (err));
459           return err;
460         }
461       raw->linkname[nread] = 0;
462     }
463   
464
465   /* Compute the checksum.  */
466   memset (raw->checksum, ' ', sizeof raw->checksum);
467   chksum = 0;
468   p = record;
469   for (n=0; n < RECORDSIZE; n++)
470     chksum += *p++;
471   store_xoctal (raw->checksum, sizeof raw->checksum - 1, chksum);
472   raw->checksum[7] = ' ';
473
474   return 0;
475 }
476
477
478 static gpg_error_t
479 write_file (estream_t stream, tar_header_t hdr)
480 {
481   gpg_error_t err;
482   char record[RECORDSIZE];
483   estream_t infp;
484   size_t nread, nbytes;
485   int any;
486
487   err = build_header (record, hdr);
488   if (err)
489     {
490       if (gpg_err_code (err) == GPG_ERR_NOT_SUPPORTED)
491         {
492           log_info ("skipping unsupported file `%s'\n", hdr->name);
493           err = 0;
494         }
495       return err;
496     }
497
498   if (hdr->typeflag == TF_REGULAR)
499     {
500       infp = es_fopen (hdr->name, "rb");
501       if (!infp)
502         {
503           err = gpg_error_from_syserror ();
504           log_error ("can't open `%s': %s - skipped\n",
505                      hdr->name, gpg_strerror (err));
506           return err;
507         }
508     }
509   else
510     infp = NULL;
511
512   err = write_record (stream, record);
513   if (err)
514     goto leave;
515
516   if (hdr->typeflag == TF_REGULAR)
517     {
518       hdr->nrecords = (hdr->size + RECORDSIZE-1)/RECORDSIZE;
519       any = 0;
520       while (hdr->nrecords--)
521         {
522           nbytes = hdr->nrecords? RECORDSIZE : (hdr->size % RECORDSIZE);
523           nread = es_fread (record, 1, nbytes, infp);
524           if (nread != nbytes)
525             {
526               err = gpg_error_from_syserror ();
527               log_error ("error reading file `%s': %s%s\n",
528                          hdr->name, gpg_strerror (err),
529                          any? " (file shrunk?)":"");
530               goto leave;
531             }
532           any = 1;
533           err = write_record (stream, record);
534           if (err)
535             goto leave;
536         }
537       nread = es_fread (record, 1, 1, infp);
538       if (nread)
539         log_info ("note: file `%s' has grown\n", hdr->name);
540     }
541
542  leave:
543   if (err)
544     es_fclose (infp);
545   else if ((err = es_fclose (infp)))
546     log_error ("error closing file `%s': %s\n", hdr->name, gpg_strerror (err));
547       
548   return err;
549 }
550
551
552 static gpg_error_t
553 write_eof_mark (estream_t stream)
554 {
555   gpg_error_t err;
556   char record[RECORDSIZE];
557
558   memset (record, 0, sizeof record);
559   err = write_record (stream, record);
560   if (!err)
561     err = write_record (stream, record);
562   return err;
563 }
564
565
566 \f
567 void
568 gpgtar_create (char **inpattern)
569 {
570   gpg_error_t err = 0;
571   const char *pattern;
572   struct scanctrl_s scanctrl_buffer;
573   scanctrl_t scanctrl = &scanctrl_buffer;
574   tar_header_t hdr, *start_tail;
575   estream_t outstream;
576
577   memset (scanctrl, 0, sizeof *scanctrl);
578   scanctrl->flist_tail = &scanctrl->flist;
579
580   for (; (pattern = *inpattern); inpattern++)
581     {
582       if (!*pattern)
583         continue;
584       if (opt.verbose > 1)
585         log_info ("scanning `%s'\n", pattern);
586
587       start_tail = scanctrl->flist_tail;
588       if (!pattern_valid_p (pattern))
589         log_error ("skipping invalid name `%s'\n", pattern);
590       else if (!add_entry (pattern, strlen (pattern), NULL, scanctrl)
591                && *start_tail && ((*start_tail)->typeflag & TF_DIRECTORY))
592         scan_recursive (pattern, scanctrl);
593     }
594
595   if (opt.outfile)
596     {
597       outstream = es_fopen (opt.outfile, "wb");
598       if (!outstream)
599         {
600           err = gpg_error_from_syserror ();
601           log_error (_("can't create `%s': %s\n"),
602                      opt.outfile, gpg_strerror (err));
603           goto leave;
604         }
605     }
606   else
607     {
608       outstream = es_stdout;
609     }
610
611   for (hdr = scanctrl->flist; hdr; hdr = hdr->next)
612     {
613       err = write_file (outstream, hdr);
614       if (err)
615         goto leave;
616     }
617   err = write_eof_mark (outstream);
618
619  leave:
620   if (!err)
621     {
622       if (outstream != es_stdout)
623         err = es_fclose (outstream);
624       else
625         err = es_fflush (outstream);
626       outstream = NULL;
627     }
628   if (err)
629     {
630       log_error ("creating tarball `%s' failed: %s\n",
631                  es_fname_get (outstream), gpg_strerror (err));
632       if (outstream && outstream != es_stdout)
633         es_fclose (outstream);
634       if (opt.outfile)
635         gnupg_remove (opt.outfile);
636     }
637   scanctrl->flist_tail = NULL;
638   while ( (hdr = scanctrl->flist) )
639     {
640       scanctrl->flist = hdr->next;
641       xfree (hdr);
642     }
643 }