test-tool: Document the -t command line option.
[libguestfs.git] / cat / virt-ls.c
1 /* virt-ls
2  * Copyright (C) 2010-2011 Red Hat Inc.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18
19 #include <config.h>
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <inttypes.h>
25 #include <unistd.h>
26 #include <getopt.h>
27 #include <fcntl.h>
28 #include <locale.h>
29 #include <assert.h>
30 #include <time.h>
31 #include <libintl.h>
32
33 #include "human.h"
34 #include "progname.h"
35
36 #include "guestfs.h"
37 #include "options.h"
38
39 /* Currently open libguestfs handle. */
40 guestfs_h *g;
41
42 int read_only = 1;
43 int live = 0;
44 int verbose = 0;
45 int keys_from_stdin = 0;
46 int echo_keys = 0;
47 const char *libvirt_uri = NULL;
48 int inspector = 1;
49
50 static int csv = 0;
51 static int human = 0;
52 static int enable_uids = 0;
53 static int enable_times = 0;
54 static int time_t_output = 0;
55 static int time_relative = 0; /* 1 = seconds, 2 = days */
56 static int enable_extra_stats = 0;
57 static const char *checksum = NULL;
58
59 static time_t now;
60
61 static int do_ls (const char *dir);
62 static int do_ls_l (const char *dir);
63 static int do_ls_R (const char *dir);
64 static int do_ls_lR (const char *dir);
65
66 static void output_start_line (void);
67 static void output_end_line (void);
68 static void output_int64 (int64_t);
69 static void output_int64_dev (int64_t);
70 static void output_int64_perms (int64_t);
71 static void output_int64_size (int64_t);
72 static void output_int64_time (int64_t);
73 static void output_int64_uid (int64_t);
74 static void output_string (const char *);
75 static void output_string_link (const char *);
76
77 static int is_reg (int64_t mode);
78 static int is_dir (int64_t mode);
79 static int is_chr (int64_t mode);
80 static int is_blk (int64_t mode);
81 static int is_fifo (int64_t mode);
82 static int is_lnk (int64_t mode);
83 static int is_sock (int64_t mode);
84
85 static size_t count_strings (char **);
86 static void free_strings (char **);
87 static char **take_strings (char **, size_t n, char ***);
88
89 static inline char *
90 bad_cast (char const *s)
91 {
92   return (char *) s;
93 }
94
95 static void __attribute__((noreturn))
96 usage (int status)
97 {
98   if (status != EXIT_SUCCESS)
99     fprintf (stderr, _("Try `%s --help' for more information.\n"),
100              program_name);
101   else {
102     fprintf (stdout,
103            _("%s: list files in a virtual machine\n"
104              "Copyright (C) 2010-2011 Red Hat Inc.\n"
105              "Usage:\n"
106              "  %s [--options] -d domname dir [dir ...]\n"
107              "  %s [--options] -a disk.img [-a disk.img ...] dir [dir ...]\n"
108              "Options:\n"
109              "  -a|--add image       Add image\n"
110              "  --checksum[=...]     Display file checksums\n"
111              "  -c|--connect uri     Specify libvirt URI for -d option\n"
112              "  --csv                Comma-Separated Values output\n"
113              "  -d|--domain guest    Add disks from libvirt guest\n"
114              "  --echo-keys          Don't turn off echo for passphrases\n"
115              "  --extra-stats        Display extra stats\n"
116              "  --format[=raw|..]    Force disk format for -a option\n"
117              "  --help               Display brief help\n"
118              "  -h|--human-readable  Human-readable sizes in output\n"
119              "  --keys-from-stdin    Read passphrases from stdin\n"
120              "  -l|--long            Long listing\n"
121              "  -R|--recursive       Recursive listing\n"
122              "  --times              Display file times\n"
123              "  --time-days          Display file times as days before now\n"
124              "  --time-relative      Display file times as seconds before now\n"
125              "  --time-t             Display file times as time_t's\n"
126              "  --uids               Display UID, GID\n"
127              "  -v|--verbose         Verbose messages\n"
128              "  -V|--version         Display version and exit\n"
129              "  -x                   Trace libguestfs API calls\n"
130              "For more information, see the manpage %s(1).\n"),
131              program_name, program_name, program_name,
132              program_name);
133   }
134   exit (status);
135 }
136
137 int
138 main (int argc, char *argv[])
139 {
140   /* Current time for --time-days, --time-relative output. */
141   time (&now);
142
143   /* Set global program name that is not polluted with libtool artifacts.  */
144   set_program_name (argv[0]);
145
146   setlocale (LC_ALL, "");
147   bindtextdomain (PACKAGE, LOCALEBASEDIR);
148   textdomain (PACKAGE);
149
150   enum { HELP_OPTION = CHAR_MAX + 1 };
151
152   static const char *options = "a:c:d:hlRvVx";
153   static const struct option long_options[] = {
154     { "add", 1, 0, 'a' },
155     { "checksum", 2, 0, 0 },
156     { "checksums", 2, 0, 0 },
157     { "csv", 0, 0, 0 },
158     { "connect", 1, 0, 'c' },
159     { "domain", 1, 0, 'd' },
160     { "echo-keys", 0, 0, 0 },
161     { "extra-stat", 0, 0, 0 },
162     { "extra-stats", 0, 0, 0 },
163     { "format", 2, 0, 0 },
164     { "help", 0, 0, HELP_OPTION },
165     { "human-readable", 0, 0, 'h' },
166     { "keys-from-stdin", 0, 0, 0 },
167     { "long", 0, 0, 'l' },
168     { "recursive", 0, 0, 'R' },
169     { "time", 0, 0, 0 },
170     { "times", 0, 0, 0 },
171     { "time-days", 0, 0, 0 },
172     { "time-relative", 0, 0, 0 },
173     { "time-t", 0, 0, 0 },
174     { "uid", 0, 0, 0 },
175     { "uids", 0, 0, 0 },
176     { "verbose", 0, 0, 'v' },
177     { "version", 0, 0, 'V' },
178     { 0, 0, 0, 0 }
179   };
180   struct drv *drvs = NULL;
181   struct drv *drv;
182   const char *format = NULL;
183   int c;
184   int option_index;
185 #define MODE_LS_L  1
186 #define MODE_LS_R  2
187 #define MODE_LS_LR (MODE_LS_L|MODE_LS_R)
188   int mode = 0;
189
190   g = guestfs_create ();
191   if (g == NULL) {
192     fprintf (stderr, _("guestfs_create: failed to create handle\n"));
193     exit (EXIT_FAILURE);
194   }
195
196   argv[0] = bad_cast (program_name);
197
198   for (;;) {
199     c = getopt_long (argc, argv, options, long_options, &option_index);
200     if (c == -1) break;
201
202     switch (c) {
203     case 0:                     /* options which are long only */
204       if (STREQ (long_options[option_index].name, "keys-from-stdin")) {
205         keys_from_stdin = 1;
206       } else if (STREQ (long_options[option_index].name, "echo-keys")) {
207         echo_keys = 1;
208       } else if (STREQ (long_options[option_index].name, "format")) {
209         if (!optarg || STREQ (optarg, ""))
210           format = NULL;
211         else
212           format = optarg;
213       } else if (STREQ (long_options[option_index].name, "checksum") ||
214                  STREQ (long_options[option_index].name, "checksums")) {
215         if (!optarg || STREQ (optarg, ""))
216           checksum = "md5";
217         else
218           checksum = optarg;
219       } else if (STREQ (long_options[option_index].name, "csv")) {
220         csv = 1;
221       } else if (STREQ (long_options[option_index].name, "extra-stat") ||
222                  STREQ (long_options[option_index].name, "extra-stats")) {
223         enable_extra_stats = 1;
224       } else if (STREQ (long_options[option_index].name, "time") ||
225                  STREQ (long_options[option_index].name, "times")) {
226         enable_times = 1;
227       } else if (STREQ (long_options[option_index].name, "time-t")) {
228         enable_times = 1;
229         time_t_output = 1;
230       } else if (STREQ (long_options[option_index].name, "time-relative")) {
231         enable_times = 1;
232         time_t_output = 1;
233         time_relative = 1;
234       } else if (STREQ (long_options[option_index].name, "time-days")) {
235         enable_times = 1;
236         time_t_output = 1;
237         time_relative = 2;
238       } else if (STREQ (long_options[option_index].name, "uid") ||
239                  STREQ (long_options[option_index].name, "uids")) {
240         enable_uids = 1;
241       } else {
242         fprintf (stderr, _("%s: unknown long option: %s (%d)\n"),
243                  program_name, long_options[option_index].name, option_index);
244         exit (EXIT_FAILURE);
245       }
246       break;
247
248     case 'a':
249       OPTION_a;
250       break;
251
252     case 'c':
253       OPTION_c;
254       break;
255
256     case 'd':
257       OPTION_d;
258       break;
259
260     case 'h':
261       human = 1;
262       break;
263
264     case 'l':
265       mode |= MODE_LS_L;
266       break;
267
268     case 'R':
269       mode |= MODE_LS_R;
270       break;
271
272     case 'v':
273       OPTION_v;
274       break;
275
276     case 'V':
277       OPTION_V;
278       break;
279
280     case 'x':
281       OPTION_x;
282       break;
283
284     case HELP_OPTION:
285       usage (EXIT_SUCCESS);
286
287     default:
288       usage (EXIT_FAILURE);
289     }
290   }
291
292   /* Old-style syntax?  There were no -a or -d options in the old
293    * virt-ls which is how we detect this.
294    */
295   if (drvs == NULL) {
296     /* argc - 1 because last parameter is the single directory name. */
297     while (optind < argc - 1) {
298       if (strchr (argv[optind], '/') ||
299           access (argv[optind], F_OK) == 0) { /* simulate -a option */
300         drv = malloc (sizeof (struct drv));
301         if (!drv) {
302           perror ("malloc");
303           exit (EXIT_FAILURE);
304         }
305         drv->type = drv_a;
306         drv->a.filename = argv[optind];
307         drv->a.format = NULL;
308         drv->next = drvs;
309         drvs = drv;
310       } else {                  /* simulate -d option */
311         drv = malloc (sizeof (struct drv));
312         if (!drv) {
313           perror ("malloc");
314           exit (EXIT_FAILURE);
315         }
316         drv->type = drv_d;
317         drv->d.guest = argv[optind];
318         drv->next = drvs;
319         drvs = drv;
320       }
321
322       optind++;
323     }
324   }
325
326   /* These are really constants, but they have to be variables for the
327    * options parsing code.  Assert here that they have known-good
328    * values.
329    */
330   assert (read_only == 1);
331   assert (inspector == 1);
332   assert (live == 0);
333
334   /* Many flags only apply to -lR mode. */
335   if (mode != MODE_LS_LR &&
336       (csv || human || enable_uids || enable_times || enable_extra_stats ||
337        checksum)) {
338     fprintf (stderr, _("%s: used a flag which can only be combined with -lR mode\nFor more information, read the virt-ls(1) man page.\n"),
339              program_name);
340     exit (EXIT_FAILURE);
341   }
342
343   /* CSV && human is unsafe because spreadsheets fail to parse these
344    * fields correctly.  (RHBZ#600977).
345    */
346   if (human && csv) {
347     fprintf (stderr, _("%s: you cannot use -h and --csv options together.\n"),
348              program_name);
349     exit (EXIT_FAILURE);
350   }
351
352   /* User must specify at least one directory name on the command line. */
353   if (optind >= argc || argc - optind < 1)
354     usage (EXIT_FAILURE);
355
356   /* User must have specified some drives. */
357   if (drvs == NULL)
358     usage (EXIT_FAILURE);
359
360   /* Add drives, inspect and mount.  Note that inspector is always true,
361    * and there is no -m option.
362    */
363   add_drives (drvs, 'a');
364
365   if (guestfs_launch (g) == -1)
366     exit (EXIT_FAILURE);
367
368   inspect_mount ();
369
370   /* Free up data structures, no longer needed after this point. */
371   free_drives (drvs);
372
373   unsigned errors = 0;
374
375   while (optind < argc) {
376     const char *dir = argv[optind];
377
378     switch (mode) {
379     case 0:                     /* no -l or -R option */
380       if (do_ls (dir) == -1)
381         errors++;
382       break;
383
384     case MODE_LS_L:             /* virt-ls -l */
385       if (do_ls_l (dir) == -1)
386         errors++;
387       break;
388
389     case MODE_LS_R:             /* virt-ls -R */
390       if (do_ls_R (dir) == -1)
391         errors++;
392       break;
393
394     case MODE_LS_LR:            /* virt-ls -lR */
395       if (do_ls_lR (dir) == -1)
396         errors++;
397       break;
398
399     default:
400       abort ();                 /* can't happen */
401     }
402
403     optind++;
404   }
405
406   guestfs_close (g);
407
408   exit (errors == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
409 }
410
411 static int
412 do_ls (const char *dir)
413 {
414   char **lines;
415   size_t i;
416
417   if ((lines = guestfs_ls (g, dir)) == NULL) {
418     return -1;
419   }
420
421   for (i = 0; lines[i] != NULL; ++i) {
422     printf ("%s\n", lines[i]);
423     free (lines[i]);
424   }
425   free (lines);
426
427   return 0;
428 }
429
430 static int
431 do_ls_l (const char *dir)
432 {
433   char *out;
434
435   if ((out = guestfs_ll (g, dir)) == NULL)
436     return -1;
437
438   printf ("%s", out);
439   free (out);
440
441   return 0;
442 }
443
444 static int
445 do_ls_R (const char *dir)
446 {
447   /* This is TMP_TEMPLATE_ON_STACK expanded from fish.h. */
448   const char *tmpdir = guestfs_tmpdir ();
449   char tmpfile[strlen (tmpdir) + 32];
450   sprintf (tmpfile, "%s/virtlsXXXXXX", tmpdir);
451
452   int fd = mkstemp (tmpfile);
453   if (fd == -1) {
454     perror ("mkstemp");
455     exit (EXIT_FAILURE);
456   }
457
458   char buf[BUFSIZ]; /* also used below */
459   snprintf (buf, sizeof buf, "/dev/fd/%d", fd);
460
461   if (guestfs_find0 (g, dir, buf) == -1)
462     return -1;
463
464   if (close (fd) == -1) {
465     perror (tmpfile);
466     exit (EXIT_FAILURE);
467   }
468
469   /* The output of find0 is a \0-separated file.  Turn each \0 into
470    * a \n character.
471    */
472   fd = open (tmpfile, O_RDONLY);
473   if (fd == -1) {
474     perror (tmpfile);
475     exit (EXIT_FAILURE);
476   }
477
478   ssize_t r;
479   while ((r = read (fd, buf, sizeof buf)) > 0) {
480     size_t i;
481     for (i = 0; i < (size_t) r; ++i)
482       if (buf[i] == '\0')
483         buf[i] = '\n';
484
485     size_t n = r;
486     while (n > 0) {
487       r = write (1, buf, n);
488       if (r == -1) {
489         perror ("write");
490         exit (EXIT_FAILURE);
491       }
492       n -= r;
493     }
494   }
495
496   if (r == -1 || close (fd) == -1) {
497     perror (tmpfile);
498     exit (EXIT_FAILURE);
499   }
500
501  unlink (tmpfile);
502
503  return 0;
504 }
505
506 /* Adapted from
507 https://rwmj.wordpress.com/2010/12/15/tip-audit-virtual-machine-for-setuid-files/
508 */
509 static char *full_path (const char *dir, const char *name);
510 static struct guestfs_stat_list *lstatlist (const char *dir, char **names);
511 static struct guestfs_xattr_list *lxattrlist (const char *dir, char **names);
512 static int show_file (const char *dir, const char *name, const struct guestfs_stat *stat, const struct guestfs_xattr_list *xattrs);
513
514 typedef int (*visitor_function) (const char *dir, const char *name, const struct guestfs_stat *stat, const struct guestfs_xattr_list *xattrs);
515
516 static int
517 visit (int depth, const char *dir, visitor_function f)
518 {
519   /* Call 'f' with the top directory.  Note that ordinary recursive
520    * visits will not otherwise do this, so we have to have a special
521    * case.
522    */
523   if (depth == 0) {
524     struct guestfs_stat *stat;
525     struct guestfs_xattr_list *xattrs;
526     int r;
527
528     stat = guestfs_lstat (g, dir);
529     if (stat == NULL)
530       return -1;
531
532     xattrs = guestfs_lgetxattrs (g, dir);
533     if (xattrs == NULL) {
534       guestfs_free_stat (stat);
535       return -1;
536     }
537
538     r = f (dir, NULL, stat, xattrs);
539     guestfs_free_stat (stat);
540     guestfs_free_xattr_list (xattrs);
541
542     if (r == -1)
543       return -1;
544   }
545
546   int ret = -1;
547   char **names = NULL;
548   char *path = NULL;
549   size_t i, xattrp;
550   struct guestfs_stat_list *stats = NULL;
551   struct guestfs_xattr_list *xattrs = NULL;
552
553   names = guestfs_ls (g, dir);
554   if (names == NULL)
555     goto out;
556
557   stats = lstatlist (dir, names);
558   if (stats == NULL)
559     goto out;
560
561   xattrs = lxattrlist (dir, names);
562   if (xattrs == NULL)
563     goto out;
564
565   /* Call function on everything in this directory. */
566   for (i = 0, xattrp = 0; names[i] != NULL; ++i, ++xattrp) {
567     struct guestfs_xattr_list file_xattrs;
568     size_t nr_xattrs;
569
570     assert (stats->len >= i);
571     assert (xattrs->len >= xattrp);
572
573     /* Find the list of extended attributes for this file. */
574     assert (strlen (xattrs->val[xattrp].attrname) == 0);
575
576     if (xattrs->val[xattrp].attrval_len == 0) {
577       fprintf (stderr, _("%s: error getting extended attrs for %s %s\n"),
578                program_name, dir, names[i]);
579       goto out;
580     }
581     /* lxattrlist function made sure attrval was \0-terminated, so we can do */
582     if (sscanf (xattrs->val[xattrp].attrval, "%zu", &nr_xattrs) != 1) {
583       fprintf (stderr, _("%s: error: cannot parse xattr count for %s %s\n"),
584                program_name, dir, names[i]);
585       goto out;
586     }
587
588     file_xattrs.len = nr_xattrs;
589     file_xattrs.val = &xattrs->val[xattrp];
590     xattrp += nr_xattrs;
591
592     /* Call the function. */
593     if (f (dir, names[i], &stats->val[i], &file_xattrs) == -1)
594       goto out;
595
596     /* Recursively call visit, but only on directories. */
597     if (is_dir (stats->val[i].mode)) {
598       path = full_path (dir, names[i]);
599       if (visit (depth + 1, path, f) == -1)
600         goto out;
601       free (path); path = NULL;
602     }
603   }
604
605   ret = 0;
606
607  out:
608   free (path);
609   if (names)
610     free_strings (names);
611   if (stats)
612     guestfs_free_stat_list (stats);
613   if (xattrs)
614     guestfs_free_xattr_list (xattrs);
615   return ret;
616 }
617
618 static char *
619 full_path (const char *dir, const char *name)
620 {
621   int r;
622   char *path;
623
624   if (STREQ (dir, "/"))
625     r = asprintf (&path, "/%s", name ? name : "");
626   else if (name)
627     r = asprintf (&path, "%s/%s", dir, name);
628   else
629     r = asprintf (&path, "%s", dir);
630
631   if (r == -1) {
632     perror ("asprintf");
633     exit (EXIT_FAILURE);
634   }
635
636   return path;
637 }
638
639 /* This calls guestfs_lstatlist, but it splits the names list up so that we
640  * don't overrun the libguestfs protocol limit.
641  */
642 #define LSTATLIST_MAX 1000
643
644 static struct guestfs_stat_list *
645 lstatlist (const char *dir, char **names)
646 {
647   size_t len = count_strings (names);
648   char **first;
649   size_t old_len;
650   struct guestfs_stat_list *ret, *stats;
651
652   ret = malloc (sizeof *ret);
653   if (ret == NULL) {
654     perror ("malloc");
655     exit (EXIT_FAILURE);
656   }
657   ret->len = 0;
658   ret->val = NULL;
659
660   while (len > 0) {
661     first = take_strings (names, LSTATLIST_MAX, &names);
662     len = len <= LSTATLIST_MAX ? 0 : len - LSTATLIST_MAX;
663
664     stats = guestfs_lstatlist (g, dir, first);
665     /* Note we don't need to free up the strings because take_strings
666      * does not do a deep copy.
667      */
668     free (first);
669
670     if (stats == NULL) {
671       free (ret);
672       return NULL;
673     }
674
675     /* Append stats to ret. */
676     old_len = ret->len;
677     ret->len += stats->len;
678     ret->val = realloc (ret->val, ret->len * sizeof (struct guestfs_stat));
679     if (ret->val == NULL) {
680       perror ("realloc");
681       exit (EXIT_FAILURE);
682     }
683     memcpy (&ret->val[old_len], stats->val,
684             stats->len * sizeof (struct guestfs_stat));
685
686     guestfs_free_stat_list (stats);
687   }
688
689   return ret;
690 }
691
692 /* Same as above, for lxattrlist.  Note the rather peculiar format
693  * used to return the list of extended attributes (see
694  * guestfs_lxattrlist documentation).
695  */
696 #define LXATTRLIST_MAX 1000
697
698 static struct guestfs_xattr_list *
699 lxattrlist (const char *dir, char **names)
700 {
701   size_t len = count_strings (names);
702   char **first;
703   size_t i, old_len;
704   struct guestfs_xattr_list *ret, *xattrs;
705
706   ret = malloc (sizeof *ret);
707   if (ret == NULL) {
708     perror ("malloc");
709     exit (EXIT_FAILURE);
710   }
711   ret->len = 0;
712   ret->val = NULL;
713
714   while (len > 0) {
715     first = take_strings (names, LXATTRLIST_MAX, &names);
716     len = len <= LXATTRLIST_MAX ? 0 : len - LXATTRLIST_MAX;
717
718     xattrs = guestfs_lxattrlist (g, dir, first);
719     /* Note we don't need to free up the strings because take_strings
720      * does not do a deep copy.
721      */
722     free (first);
723
724     if (xattrs == NULL) {
725       free (ret);
726       return NULL;
727     }
728
729     /* Append xattrs to ret. */
730     old_len = ret->len;
731     ret->len += xattrs->len;
732     ret->val = realloc (ret->val, ret->len * sizeof (struct guestfs_xattr));
733     if (ret->val == NULL) {
734       perror ("realloc");
735       exit (EXIT_FAILURE);
736     }
737     for (i = 0; i < xattrs->len; ++i, ++old_len) {
738       /* We have to make a deep copy of the attribute name and value.
739        * The attrval contains 8 bit data.  However make sure also that
740        * it is \0-terminated, because that makes the calling code
741        * simpler.
742        */
743       ret->val[old_len].attrname = strdup (xattrs->val[i].attrname);
744       ret->val[old_len].attrval = malloc (xattrs->val[i].attrval_len + 1);
745       if (ret->val[old_len].attrname == NULL ||
746           ret->val[old_len].attrval == NULL) {
747         perror ("malloc");
748         exit (EXIT_FAILURE);
749       }
750       ret->val[old_len].attrval_len = xattrs->val[i].attrval_len;
751       memcpy (ret->val[old_len].attrval, xattrs->val[i].attrval,
752               xattrs->val[i].attrval_len);
753       ret->val[i].attrval[ret->val[i].attrval_len] = '\0';
754     }
755
756     guestfs_free_xattr_list (xattrs);
757   }
758
759   return ret;
760 }
761
762 static int
763 do_ls_lR (const char *dir)
764 {
765   return visit (0, dir, show_file);
766 }
767
768 /* This is the function which is called to display all files and
769  * directories, and it's where the magic happens.  We are called with
770  * full stat and extended attributes for each file, so there is no
771  * penalty for displaying anything in those structures.  However if we
772  * need other things (eg. checksum) we may have to go back to the
773  * appliance and then there can be a very large penalty.
774  */
775 static int
776 show_file (const char *dir, const char *name,
777            const struct guestfs_stat *stat,
778            const struct guestfs_xattr_list *xattrs)
779 {
780   char filetype[2];
781   char *path = NULL, *csum = NULL, *link = NULL;
782
783   /* Display the basic fields. */
784   output_start_line ();
785
786   if (is_reg (stat->mode))
787     filetype[0] = '-';
788   else if (is_dir (stat->mode))
789     filetype[0] = 'd';
790   else if (is_chr (stat->mode))
791     filetype[0] = 'c';
792   else if (is_blk (stat->mode))
793     filetype[0] = 'b';
794   else if (is_fifo (stat->mode))
795     filetype[0] = 'p';
796   else if (is_lnk (stat->mode))
797     filetype[0] = 'l';
798   else if (is_sock (stat->mode))
799     filetype[0] = 's';
800   else
801     filetype[0] = 'u';
802   filetype[1] = '\0';
803   output_string (filetype);
804   output_int64_perms (stat->mode & 07777);
805
806   output_int64_size (stat->size);
807
808   /* Display extra fields when enabled. */
809   if (enable_uids) {
810     output_int64_uid (stat->uid);
811     output_int64_uid (stat->gid);
812   }
813
814   if (enable_times) {
815     output_int64_time (stat->atime);
816     output_int64_time (stat->mtime);
817     output_int64_time (stat->ctime);
818   }
819
820   if (enable_extra_stats) {
821     output_int64_dev (stat->dev);
822     output_int64 (stat->ino);
823     output_int64 (stat->nlink);
824     output_int64_dev (stat->rdev);
825     output_int64 (stat->blocks);
826   }
827
828   /* Disabled for now -- user would definitely want these to be interpreted.
829   if (enable_xattrs)
830     output_xattrs (xattrs);
831   */
832
833   if (checksum && is_reg (stat->mode)) {
834     csum = guestfs_checksum (g, checksum, path);
835     if (!csum)
836       exit (EXIT_FAILURE);
837
838     output_string (csum);
839   }
840
841   path = full_path (dir, name);
842   output_string (path);
843
844   if (is_lnk (stat->mode))
845     /* XXX Fix this for NTFS. */
846     link = guestfs_readlink (g, path);
847   if (link)
848     output_string_link (link);
849
850   output_end_line ();
851
852   free (path);
853   free (csum);
854   free (link);
855
856   return 0;
857 }
858
859 /* Output functions.
860  *
861  * Note that we have to be careful to check return values from printf
862  * in these functions, because we want to catch ENOSPC errors.
863  */
864 static int field;
865 static void
866 next_field (void)
867 {
868   int c = csv ? ',' : ' ';
869
870   field++;
871   if (field == 1) return;
872
873   if (putchar (c) == EOF) {
874     perror ("putchar");
875     exit (EXIT_FAILURE);
876   }
877 }
878
879 static void
880 output_start_line (void)
881 {
882   field = 0;
883 }
884
885 static void
886 output_end_line (void)
887 {
888   if (printf ("\n") < 0) {
889     perror ("printf");
890     exit (EXIT_FAILURE);
891   }
892 }
893
894 static void
895 output_string (const char *s)
896 {
897   next_field ();
898
899   if (!csv) {
900   print_no_quoting:
901     if (printf ("%s", s) < 0) {
902       perror ("printf");
903       exit (EXIT_FAILURE);
904     }
905   }
906   else {
907     /* Quote CSV string without requiring an external module. */
908     size_t i, len;
909     int needs_quoting = 0;
910
911     len = strlen (s);
912
913     for (i = 0; i < len; ++i) {
914       if (s[i] == ' ' || s[i] == '"' ||
915           s[i] == '\n' || s[i] == ',') {
916         needs_quoting = 1;
917         break;
918       }
919     }
920
921     if (!needs_quoting)
922       goto print_no_quoting;
923
924     /* Quoting for CSV fields. */
925     if (putchar ('"') == EOF) {
926       perror ("putchar");
927       exit (EXIT_FAILURE);
928     }
929     for (i = 0; i < len; ++i) {
930       if (s[i] == '"') {
931         if (putchar ('"') == EOF || putchar ('"') == EOF) {
932           perror ("putchar");
933           exit (EXIT_FAILURE);
934         }
935       } else {
936         if (putchar (s[i]) == EOF) {
937           perror ("putchar");
938           exit (EXIT_FAILURE);
939         }
940       }
941     }
942     if (putchar ('"') == EOF) {
943       perror ("putchar");
944       exit (EXIT_FAILURE);
945     }
946   }
947 }
948
949 static void
950 output_string_link (const char *link)
951 {
952   if (csv)
953     output_string (link);
954   else {
955     next_field ();
956
957     if (printf ("-> %s", link) < 0) {
958       perror ("printf");
959       exit (EXIT_FAILURE);
960     }
961   }
962 }
963
964 static void
965 output_int64 (int64_t i)
966 {
967   next_field ();
968   /* csv doesn't need escaping */
969   if (printf ("%" PRIi64, i) < 0) {
970     perror ("printf");
971     exit (EXIT_FAILURE);
972   }
973 }
974
975 static void
976 output_int64_size (int64_t size)
977 {
978   char buf[LONGEST_HUMAN_READABLE];
979   int hopts = human_round_to_nearest|human_autoscale|human_base_1024|human_SI;
980   int r;
981
982   next_field ();
983
984   if (!csv) {
985     if (!human)
986       r = printf ("%10" PRIi64, size);
987     else
988       r = printf ("%10s",
989                   human_readable ((uintmax_t) size, buf, hopts, 1, 1));
990   } else {
991     /* CSV is the same as non-CSV but we don't need to right-align. */
992     if (!human)
993       r = printf ("%" PRIi64, size);
994     else
995       r = printf ("%s",
996                   human_readable ((uintmax_t) size, buf, hopts, 1, 1));
997   }
998
999   if (r < 0) {
1000     perror ("printf");
1001     exit (EXIT_FAILURE);
1002   }
1003 }
1004
1005 static void
1006 output_int64_perms (int64_t i)
1007 {
1008   next_field ();
1009   /* csv doesn't need escaping */
1010   if (printf ("%04" PRIo64, i) < 0) {
1011     perror ("printf");
1012     exit (EXIT_FAILURE);
1013   }
1014 }
1015
1016 static void
1017 output_int64_time (int64_t i)
1018 {
1019   int r;
1020
1021   next_field ();
1022
1023   /* csv doesn't need escaping */
1024   if (time_t_output) {
1025     switch (time_relative) {
1026     case 0:                     /* --time-t */
1027       r = printf ("%10" PRIi64, i);
1028       break;
1029     case 1:                     /* --time-relative */
1030       r = printf ("%8" PRIi64, now - i);
1031       break;
1032     case 2:                     /* --time-days */
1033     default:
1034       r = printf ("%3" PRIi64, (now - i) / 86400);
1035       break;
1036     }
1037   }
1038   else {
1039     time_t t = (time_t) i;
1040     char buf[64];
1041     struct tm *tm;
1042
1043     tm = localtime (&t);
1044     if (tm == NULL) {
1045       perror ("localtime");
1046       exit (EXIT_FAILURE);
1047     }
1048
1049     if (strftime (buf, sizeof buf, "%F %T", tm) == 0) {
1050       perror ("strftime");
1051       exit (EXIT_FAILURE);
1052     }
1053
1054     r = printf ("%s", buf);
1055   }
1056
1057   if (r < 0) {
1058     perror ("printf");
1059     exit (EXIT_FAILURE);
1060   }
1061 }
1062
1063 static void
1064 output_int64_uid (int64_t i)
1065 {
1066   next_field ();
1067   /* csv doesn't need escaping */
1068   if (printf ("%4" PRIi64, i) < 0) {
1069     perror ("printf");
1070     exit (EXIT_FAILURE);
1071   }
1072 }
1073
1074 static void
1075 output_int64_dev (int64_t i)
1076 {
1077   dev_t dev = i;
1078
1079   next_field ();
1080
1081   /* csv doesn't need escaping */
1082   if (printf ("%d:%d", major (dev), minor (dev)) < 0) {
1083     perror ("printf");
1084     exit (EXIT_FAILURE);
1085   }
1086 }
1087
1088 /* In the libguestfs API, modes returned by lstat and friends are
1089  * defined to contain Linux ABI values.  However since the "current
1090  * operating system" might not be Linux, we have to hard-code those
1091  * numbers here.
1092  */
1093 static int
1094 is_reg (int64_t mode)
1095 {
1096   return (mode & 0170000) == 0100000;
1097 }
1098
1099 static int
1100 is_dir (int64_t mode)
1101 {
1102   return (mode & 0170000) == 0040000;
1103 }
1104
1105 static int
1106 is_chr (int64_t mode)
1107 {
1108   return (mode & 0170000) == 0020000;
1109 }
1110
1111 static int
1112 is_blk (int64_t mode)
1113 {
1114   return (mode & 0170000) == 0060000;
1115 }
1116
1117 static int
1118 is_fifo (int64_t mode)
1119 {
1120   return (mode & 0170000) == 0010000;
1121 }
1122
1123 /* symbolic link */
1124 static int
1125 is_lnk (int64_t mode)
1126 {
1127   return (mode & 0170000) == 0120000;
1128 }
1129
1130 static int
1131 is_sock (int64_t mode)
1132 {
1133   return (mode & 0170000) == 0140000;
1134 }
1135
1136 /* String functions. */
1137 static size_t
1138 count_strings (char **names)
1139 {
1140   size_t ret = 0;
1141
1142   while (names[ret] != NULL)
1143     ret++;
1144   return ret;
1145 }
1146
1147 static void
1148 free_strings (char **names)
1149 {
1150   size_t i;
1151
1152   for (i = 0; names[i] != NULL; ++i)
1153     free (names[i]);
1154   free (names);
1155 }
1156
1157 /* Take the first 'n' names, returning a newly allocated list.  The
1158  * strings themselves are not duplicated.  If 'lastp' is not NULL,
1159  * then it is updated with the pointer to the list of remaining names.
1160  */
1161 static char **
1162 take_strings (char **names, size_t n, char ***lastp)
1163 {
1164   size_t i;
1165
1166   char **ret = malloc ((n+1) * sizeof (char *));
1167   if (ret == NULL) {
1168     perror ("malloc");
1169     exit (EXIT_FAILURE);
1170   }
1171
1172   for (i = 0; names[i] != NULL && i < n; ++i)
1173     ret[i] = names[i];
1174
1175   ret[i] = NULL;
1176
1177   if (lastp)
1178     *lastp = &names[i];
1179
1180   return ret;
1181 }