2 * Copyright (C) 2010-2011 Red Hat Inc.
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.
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.
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.
39 /* Currently open libguestfs handle. */
45 int keys_from_stdin = 0;
47 const char *libvirt_uri = NULL;
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;
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);
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 *);
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);
85 static size_t count_strings (char **);
86 static void free_strings (char **);
87 static char **take_strings (char **, size_t n, char ***);
90 bad_cast (char const *s)
95 static void __attribute__((noreturn))
98 if (status != EXIT_SUCCESS)
99 fprintf (stderr, _("Try `%s --help' for more information.\n"),
103 _("%s: list files in a virtual machine\n"
104 "Copyright (C) 2010-2011 Red Hat Inc.\n"
106 " %s [--options] -d domname dir [dir ...]\n"
107 " %s [--options] -a disk.img [-a disk.img ...] dir [dir ...]\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,
138 main (int argc, char *argv[])
140 /* Current time for --time-days, --time-relative output. */
143 /* Set global program name that is not polluted with libtool artifacts. */
144 set_program_name (argv[0]);
146 setlocale (LC_ALL, "");
147 bindtextdomain (PACKAGE, LOCALEBASEDIR);
148 textdomain (PACKAGE);
150 enum { HELP_OPTION = CHAR_MAX + 1 };
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 },
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' },
170 { "times", 0, 0, 0 },
171 { "time-days", 0, 0, 0 },
172 { "time-relative", 0, 0, 0 },
173 { "time-t", 0, 0, 0 },
176 { "verbose", 0, 0, 'v' },
177 { "version", 0, 0, 'V' },
180 struct drv *drvs = NULL;
182 const char *format = NULL;
187 #define MODE_LS_LR (MODE_LS_L|MODE_LS_R)
190 g = guestfs_create ();
192 fprintf (stderr, _("guestfs_create: failed to create handle\n"));
196 argv[0] = bad_cast (program_name);
199 c = getopt_long (argc, argv, options, long_options, &option_index);
203 case 0: /* options which are long only */
204 if (STREQ (long_options[option_index].name, "keys-from-stdin")) {
206 } else if (STREQ (long_options[option_index].name, "echo-keys")) {
208 } else if (STREQ (long_options[option_index].name, "format")) {
209 if (!optarg || STREQ (optarg, ""))
213 } else if (STREQ (long_options[option_index].name, "checksum") ||
214 STREQ (long_options[option_index].name, "checksums")) {
215 if (!optarg || STREQ (optarg, ""))
219 } else if (STREQ (long_options[option_index].name, "csv")) {
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")) {
227 } else if (STREQ (long_options[option_index].name, "time-t")) {
230 } else if (STREQ (long_options[option_index].name, "time-relative")) {
234 } else if (STREQ (long_options[option_index].name, "time-days")) {
238 } else if (STREQ (long_options[option_index].name, "uid") ||
239 STREQ (long_options[option_index].name, "uids")) {
242 fprintf (stderr, _("%s: unknown long option: %s (%d)\n"),
243 program_name, long_options[option_index].name, option_index);
285 usage (EXIT_SUCCESS);
288 usage (EXIT_FAILURE);
292 /* Old-style syntax? There were no -a or -d options in the old
293 * virt-ls which is how we detect this.
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));
306 drv->a.filename = argv[optind];
307 drv->a.format = NULL;
310 } else { /* simulate -d option */
311 drv = malloc (sizeof (struct drv));
317 drv->d.guest = argv[optind];
326 /* These are really constants, but they have to be variables for the
327 * options parsing code. Assert here that they have known-good
330 assert (read_only == 1);
331 assert (inspector == 1);
334 /* Many flags only apply to -lR mode. */
335 if (mode != MODE_LS_LR &&
336 (csv || human || enable_uids || enable_times || enable_extra_stats ||
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"),
343 /* CSV && human is unsafe because spreadsheets fail to parse these
344 * fields correctly. (RHBZ#600977).
347 fprintf (stderr, _("%s: you cannot use -h and --csv options together.\n"),
352 /* User must specify at least one directory name on the command line. */
353 if (optind >= argc || argc - optind < 1)
354 usage (EXIT_FAILURE);
356 /* User must have specified some drives. */
358 usage (EXIT_FAILURE);
360 /* Add drives, inspect and mount. Note that inspector is always true,
361 * and there is no -m option.
363 add_drives (drvs, 'a');
365 if (guestfs_launch (g) == -1)
370 /* Free up data structures, no longer needed after this point. */
375 while (optind < argc) {
376 const char *dir = argv[optind];
379 case 0: /* no -l or -R option */
380 if (do_ls (dir) == -1)
384 case MODE_LS_L: /* virt-ls -l */
385 if (do_ls_l (dir) == -1)
389 case MODE_LS_R: /* virt-ls -R */
390 if (do_ls_R (dir) == -1)
394 case MODE_LS_LR: /* virt-ls -lR */
395 if (do_ls_lR (dir) == -1)
400 abort (); /* can't happen */
408 exit (errors == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
412 do_ls (const char *dir)
417 if ((lines = guestfs_ls (g, dir)) == NULL) {
421 for (i = 0; lines[i] != NULL; ++i) {
422 printf ("%s\n", lines[i]);
431 do_ls_l (const char *dir)
435 if ((out = guestfs_ll (g, dir)) == NULL)
445 do_ls_R (const char *dir)
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);
452 int fd = mkstemp (tmpfile);
458 char buf[BUFSIZ]; /* also used below */
459 snprintf (buf, sizeof buf, "/dev/fd/%d", fd);
461 if (guestfs_find0 (g, dir, buf) == -1)
464 if (close (fd) == -1) {
469 /* The output of find0 is a \0-separated file. Turn each \0 into
472 fd = open (tmpfile, O_RDONLY);
479 while ((r = read (fd, buf, sizeof buf)) > 0) {
481 for (i = 0; i < (size_t) r; ++i)
487 r = write (1, buf, n);
496 if (r == -1 || close (fd) == -1) {
507 https://rwmj.wordpress.com/2010/12/15/tip-audit-virtual-machine-for-setuid-files/
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);
514 typedef int (*visitor_function) (const char *dir, const char *name, const struct guestfs_stat *stat, const struct guestfs_xattr_list *xattrs);
517 visit (int depth, const char *dir, visitor_function f)
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
524 struct guestfs_stat *stat;
525 struct guestfs_xattr_list *xattrs;
528 stat = guestfs_lstat (g, dir);
532 xattrs = guestfs_lgetxattrs (g, dir);
533 if (xattrs == NULL) {
534 guestfs_free_stat (stat);
538 r = f (dir, NULL, stat, xattrs);
539 guestfs_free_stat (stat);
540 guestfs_free_xattr_list (xattrs);
550 struct guestfs_stat_list *stats = NULL;
551 struct guestfs_xattr_list *xattrs = NULL;
553 names = guestfs_ls (g, dir);
557 stats = lstatlist (dir, names);
561 xattrs = lxattrlist (dir, names);
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;
570 assert (stats->len >= i);
571 assert (xattrs->len >= xattrp);
573 /* Find the list of extended attributes for this file. */
574 assert (strlen (xattrs->val[xattrp].attrname) == 0);
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]);
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]);
588 file_xattrs.len = nr_xattrs;
589 file_xattrs.val = &xattrs->val[xattrp];
592 /* Call the function. */
593 if (f (dir, names[i], &stats->val[i], &file_xattrs) == -1)
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)
601 free (path); path = NULL;
610 free_strings (names);
612 guestfs_free_stat_list (stats);
614 guestfs_free_xattr_list (xattrs);
619 full_path (const char *dir, const char *name)
624 if (STREQ (dir, "/"))
625 r = asprintf (&path, "/%s", name ? name : "");
627 r = asprintf (&path, "%s/%s", dir, name);
629 r = asprintf (&path, "%s", dir);
639 /* This calls guestfs_lstatlist, but it splits the names list up so that we
640 * don't overrun the libguestfs protocol limit.
642 #define LSTATLIST_MAX 1000
644 static struct guestfs_stat_list *
645 lstatlist (const char *dir, char **names)
647 size_t len = count_strings (names);
650 struct guestfs_stat_list *ret, *stats;
652 ret = malloc (sizeof *ret);
661 first = take_strings (names, LSTATLIST_MAX, &names);
662 len = len <= LSTATLIST_MAX ? 0 : len - LSTATLIST_MAX;
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.
675 /* Append stats to ret. */
677 ret->len += stats->len;
678 ret->val = realloc (ret->val, ret->len * sizeof (struct guestfs_stat));
679 if (ret->val == NULL) {
683 memcpy (&ret->val[old_len], stats->val,
684 stats->len * sizeof (struct guestfs_stat));
686 guestfs_free_stat_list (stats);
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).
696 #define LXATTRLIST_MAX 1000
698 static struct guestfs_xattr_list *
699 lxattrlist (const char *dir, char **names)
701 size_t len = count_strings (names);
704 struct guestfs_xattr_list *ret, *xattrs;
706 ret = malloc (sizeof *ret);
715 first = take_strings (names, LXATTRLIST_MAX, &names);
716 len = len <= LXATTRLIST_MAX ? 0 : len - LXATTRLIST_MAX;
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.
724 if (xattrs == NULL) {
729 /* Append xattrs to ret. */
731 ret->len += xattrs->len;
732 ret->val = realloc (ret->val, ret->len * sizeof (struct guestfs_xattr));
733 if (ret->val == NULL) {
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
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) {
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';
756 guestfs_free_xattr_list (xattrs);
763 do_ls_lR (const char *dir)
765 return visit (0, dir, show_file);
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.
776 show_file (const char *dir, const char *name,
777 const struct guestfs_stat *stat,
778 const struct guestfs_xattr_list *xattrs)
781 char *path = NULL, *csum = NULL, *link = NULL;
783 /* Display the basic fields. */
784 output_start_line ();
786 if (is_reg (stat->mode))
788 else if (is_dir (stat->mode))
790 else if (is_chr (stat->mode))
792 else if (is_blk (stat->mode))
794 else if (is_fifo (stat->mode))
796 else if (is_lnk (stat->mode))
798 else if (is_sock (stat->mode))
803 output_string (filetype);
804 output_int64_perms (stat->mode & 07777);
806 output_int64_size (stat->size);
808 /* Display extra fields when enabled. */
810 output_int64_uid (stat->uid);
811 output_int64_uid (stat->gid);
815 output_int64_time (stat->atime);
816 output_int64_time (stat->mtime);
817 output_int64_time (stat->ctime);
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);
828 /* Disabled for now -- user would definitely want these to be interpreted.
830 output_xattrs (xattrs);
833 if (checksum && is_reg (stat->mode)) {
834 csum = guestfs_checksum (g, checksum, path);
838 output_string (csum);
841 path = full_path (dir, name);
842 output_string (path);
844 if (is_lnk (stat->mode))
845 /* XXX Fix this for NTFS. */
846 link = guestfs_readlink (g, path);
848 output_string_link (link);
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.
868 int c = csv ? ',' : ' ';
871 if (field == 1) return;
873 if (putchar (c) == EOF) {
880 output_start_line (void)
886 output_end_line (void)
888 if (printf ("\n") < 0) {
895 output_string (const char *s)
901 if (printf ("%s", s) < 0) {
907 /* Quote CSV string without requiring an external module. */
909 int needs_quoting = 0;
913 for (i = 0; i < len; ++i) {
914 if (s[i] == ' ' || s[i] == '"' ||
915 s[i] == '\n' || s[i] == ',') {
922 goto print_no_quoting;
924 /* Quoting for CSV fields. */
925 if (putchar ('"') == EOF) {
929 for (i = 0; i < len; ++i) {
931 if (putchar ('"') == EOF || putchar ('"') == EOF) {
936 if (putchar (s[i]) == EOF) {
942 if (putchar ('"') == EOF) {
950 output_string_link (const char *link)
953 output_string (link);
957 if (printf ("-> %s", link) < 0) {
965 output_int64 (int64_t i)
968 /* csv doesn't need escaping */
969 if (printf ("%" PRIi64, i) < 0) {
976 output_int64_size (int64_t size)
978 char buf[LONGEST_HUMAN_READABLE];
979 int hopts = human_round_to_nearest|human_autoscale|human_base_1024|human_SI;
986 r = printf ("%10" PRIi64, size);
989 human_readable ((uintmax_t) size, buf, hopts, 1, 1));
991 /* CSV is the same as non-CSV but we don't need to right-align. */
993 r = printf ("%" PRIi64, size);
996 human_readable ((uintmax_t) size, buf, hopts, 1, 1));
1001 exit (EXIT_FAILURE);
1006 output_int64_perms (int64_t i)
1009 /* csv doesn't need escaping */
1010 if (printf ("%04" PRIo64, i) < 0) {
1012 exit (EXIT_FAILURE);
1017 output_int64_time (int64_t i)
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);
1029 case 1: /* --time-relative */
1030 r = printf ("%8" PRIi64, now - i);
1032 case 2: /* --time-days */
1034 r = printf ("%3" PRIi64, (now - i) / 86400);
1039 time_t t = (time_t) i;
1043 tm = localtime (&t);
1045 perror ("localtime");
1046 exit (EXIT_FAILURE);
1049 if (strftime (buf, sizeof buf, "%F %T", tm) == 0) {
1050 perror ("strftime");
1051 exit (EXIT_FAILURE);
1054 r = printf ("%s", buf);
1059 exit (EXIT_FAILURE);
1064 output_int64_uid (int64_t i)
1067 /* csv doesn't need escaping */
1068 if (printf ("%4" PRIi64, i) < 0) {
1070 exit (EXIT_FAILURE);
1075 output_int64_dev (int64_t i)
1081 /* csv doesn't need escaping */
1082 if (printf ("%d:%d", major (dev), minor (dev)) < 0) {
1084 exit (EXIT_FAILURE);
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
1094 is_reg (int64_t mode)
1096 return (mode & 0170000) == 0100000;
1100 is_dir (int64_t mode)
1102 return (mode & 0170000) == 0040000;
1106 is_chr (int64_t mode)
1108 return (mode & 0170000) == 0020000;
1112 is_blk (int64_t mode)
1114 return (mode & 0170000) == 0060000;
1118 is_fifo (int64_t mode)
1120 return (mode & 0170000) == 0010000;
1125 is_lnk (int64_t mode)
1127 return (mode & 0170000) == 0120000;
1131 is_sock (int64_t mode)
1133 return (mode & 0170000) == 0140000;
1136 /* String functions. */
1138 count_strings (char **names)
1142 while (names[ret] != NULL)
1148 free_strings (char **names)
1152 for (i = 0; names[i] != NULL; ++i)
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.
1162 take_strings (char **names, size_t n, char ***lastp)
1166 char **ret = malloc ((n+1) * sizeof (char *));
1169 exit (EXIT_FAILURE);
1172 for (i = 0; names[i] != NULL && i < n; ++i)