Skip to content
Fetching contributors…
Cannot retrieve contributors at this time
1186 lines (1004 sloc) 27.9 KB
/* virt-ls
* Copyright (C) 2010-2012 Red Hat Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <unistd.h>
#include <getopt.h>
#include <fcntl.h>
#include <locale.h>
#include <assert.h>
#include <time.h>
#include <libintl.h>
#include "human.h"
#include "progname.h"
#include "guestfs.h"
#include "options.h"
#ifndef O_CLOEXEC
#define O_CLOEXEC 0
#endif
/* Currently open libguestfs handle. */
guestfs_h *g;
int read_only = 1;
int live = 0;
int verbose = 0;
int keys_from_stdin = 0;
int echo_keys = 0;
const char *libvirt_uri = NULL;
int inspector = 1;
static int csv = 0;
static int human = 0;
static int enable_uids = 0;
static int enable_times = 0;
static int time_t_output = 0;
static int time_relative = 0; /* 1 = seconds, 2 = days */
static int enable_extra_stats = 0;
static const char *checksum = NULL;
static time_t now;
static int do_ls (const char *dir);
static int do_ls_l (const char *dir);
static int do_ls_R (const char *dir);
static int do_ls_lR (const char *dir);
static void output_start_line (void);
static void output_end_line (void);
static void output_int64 (int64_t);
static void output_int64_dev (int64_t);
static void output_int64_perms (int64_t);
static void output_int64_size (int64_t);
static void output_int64_time (int64_t);
static void output_int64_uid (int64_t);
static void output_string (const char *);
static void output_string_link (const char *);
static int is_reg (int64_t mode);
static int is_dir (int64_t mode);
static int is_chr (int64_t mode);
static int is_blk (int64_t mode);
static int is_fifo (int64_t mode);
static int is_lnk (int64_t mode);
static int is_sock (int64_t mode);
static size_t count_strings (char **);
static void free_strings (char **);
static char **take_strings (char **, size_t n, char ***);
static inline char *
bad_cast (char const *s)
{
return (char *) s;
}
static void __attribute__((noreturn))
usage (int status)
{
if (status != EXIT_SUCCESS)
fprintf (stderr, _("Try `%s --help' for more information.\n"),
program_name);
else {
fprintf (stdout,
_("%s: list files in a virtual machine\n"
"Copyright (C) 2010-2012 Red Hat Inc.\n"
"Usage:\n"
" %s [--options] -d domname dir [dir ...]\n"
" %s [--options] -a disk.img [-a disk.img ...] dir [dir ...]\n"
"Options:\n"
" -a|--add image Add image\n"
" --checksum[=...] Display file checksums\n"
" -c|--connect uri Specify libvirt URI for -d option\n"
" --csv Comma-Separated Values output\n"
" -d|--domain guest Add disks from libvirt guest\n"
" --echo-keys Don't turn off echo for passphrases\n"
" --extra-stats Display extra stats\n"
" --format[=raw|..] Force disk format for -a option\n"
" --help Display brief help\n"
" -h|--human-readable Human-readable sizes in output\n"
" --keys-from-stdin Read passphrases from stdin\n"
" -l|--long Long listing\n"
" -R|--recursive Recursive listing\n"
" --times Display file times\n"
" --time-days Display file times as days before now\n"
" --time-relative Display file times as seconds before now\n"
" --time-t Display file times as time_t's\n"
" --uids Display UID, GID\n"
" -v|--verbose Verbose messages\n"
" -V|--version Display version and exit\n"
" -x Trace libguestfs API calls\n"
"For more information, see the manpage %s(1).\n"),
program_name, program_name, program_name,
program_name);
}
exit (status);
}
int
main (int argc, char *argv[])
{
/* Current time for --time-days, --time-relative output. */
time (&now);
/* Set global program name that is not polluted with libtool artifacts. */
set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEBASEDIR);
textdomain (PACKAGE);
enum { HELP_OPTION = CHAR_MAX + 1 };
static const char *options = "a:c:d:hlRvVx";
static const struct option long_options[] = {
{ "add", 1, 0, 'a' },
{ "checksum", 2, 0, 0 },
{ "checksums", 2, 0, 0 },
{ "csv", 0, 0, 0 },
{ "connect", 1, 0, 'c' },
{ "domain", 1, 0, 'd' },
{ "echo-keys", 0, 0, 0 },
{ "extra-stat", 0, 0, 0 },
{ "extra-stats", 0, 0, 0 },
{ "format", 2, 0, 0 },
{ "help", 0, 0, HELP_OPTION },
{ "human-readable", 0, 0, 'h' },
{ "keys-from-stdin", 0, 0, 0 },
{ "long", 0, 0, 'l' },
{ "recursive", 0, 0, 'R' },
{ "time", 0, 0, 0 },
{ "times", 0, 0, 0 },
{ "time-days", 0, 0, 0 },
{ "time-relative", 0, 0, 0 },
{ "time-t", 0, 0, 0 },
{ "uid", 0, 0, 0 },
{ "uids", 0, 0, 0 },
{ "verbose", 0, 0, 'v' },
{ "version", 0, 0, 'V' },
{ 0, 0, 0, 0 }
};
struct drv *drvs = NULL;
struct drv *drv;
const char *format = NULL;
int c;
int option_index;
#define MODE_LS_L 1
#define MODE_LS_R 2
#define MODE_LS_LR (MODE_LS_L|MODE_LS_R)
int mode = 0;
g = guestfs_create ();
if (g == NULL) {
fprintf (stderr, _("guestfs_create: failed to create handle\n"));
exit (EXIT_FAILURE);
}
argv[0] = bad_cast (program_name);
for (;;) {
c = getopt_long (argc, argv, options, long_options, &option_index);
if (c == -1) break;
switch (c) {
case 0: /* options which are long only */
if (STREQ (long_options[option_index].name, "keys-from-stdin")) {
keys_from_stdin = 1;
} else if (STREQ (long_options[option_index].name, "echo-keys")) {
echo_keys = 1;
} else if (STREQ (long_options[option_index].name, "format")) {
if (!optarg || STREQ (optarg, ""))
format = NULL;
else
format = optarg;
} else if (STREQ (long_options[option_index].name, "checksum") ||
STREQ (long_options[option_index].name, "checksums")) {
if (!optarg || STREQ (optarg, ""))
checksum = "md5";
else
checksum = optarg;
} else if (STREQ (long_options[option_index].name, "csv")) {
csv = 1;
} else if (STREQ (long_options[option_index].name, "extra-stat") ||
STREQ (long_options[option_index].name, "extra-stats")) {
enable_extra_stats = 1;
} else if (STREQ (long_options[option_index].name, "time") ||
STREQ (long_options[option_index].name, "times")) {
enable_times = 1;
} else if (STREQ (long_options[option_index].name, "time-t")) {
enable_times = 1;
time_t_output = 1;
} else if (STREQ (long_options[option_index].name, "time-relative")) {
enable_times = 1;
time_t_output = 1;
time_relative = 1;
} else if (STREQ (long_options[option_index].name, "time-days")) {
enable_times = 1;
time_t_output = 1;
time_relative = 2;
} else if (STREQ (long_options[option_index].name, "uid") ||
STREQ (long_options[option_index].name, "uids")) {
enable_uids = 1;
} else {
fprintf (stderr, _("%s: unknown long option: %s (%d)\n"),
program_name, long_options[option_index].name, option_index);
exit (EXIT_FAILURE);
}
break;
case 'a':
OPTION_a;
break;
case 'c':
OPTION_c;
break;
case 'd':
OPTION_d;
break;
case 'h':
human = 1;
break;
case 'l':
mode |= MODE_LS_L;
break;
case 'R':
mode |= MODE_LS_R;
break;
case 'v':
OPTION_v;
break;
case 'V':
OPTION_V;
break;
case 'x':
OPTION_x;
break;
case HELP_OPTION:
usage (EXIT_SUCCESS);
default:
usage (EXIT_FAILURE);
}
}
/* Old-style syntax? There were no -a or -d options in the old
* virt-ls which is how we detect this.
*/
if (drvs == NULL) {
/* argc - 1 because last parameter is the single directory name. */
while (optind < argc - 1) {
if (strchr (argv[optind], '/') ||
access (argv[optind], F_OK) == 0) { /* simulate -a option */
drv = calloc (1, sizeof (struct drv));
if (!drv) {
perror ("malloc");
exit (EXIT_FAILURE);
}
drv->type = drv_a;
drv->a.filename = argv[optind];
drv->next = drvs;
drvs = drv;
} else { /* simulate -d option */
drv = calloc (1, sizeof (struct drv));
if (!drv) {
perror ("malloc");
exit (EXIT_FAILURE);
}
drv->type = drv_d;
drv->d.guest = argv[optind];
drv->next = drvs;
drvs = drv;
}
optind++;
}
}
/* These are really constants, but they have to be variables for the
* options parsing code. Assert here that they have known-good
* values.
*/
assert (read_only == 1);
assert (inspector == 1);
assert (live == 0);
/* Many flags only apply to -lR mode. */
if (mode != MODE_LS_LR &&
(csv || human || enable_uids || enable_times || enable_extra_stats ||
checksum)) {
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"),
program_name);
exit (EXIT_FAILURE);
}
/* CSV && human is unsafe because spreadsheets fail to parse these
* fields correctly. (RHBZ#600977).
*/
if (human && csv) {
fprintf (stderr, _("%s: you cannot use -h and --csv options together.\n"),
program_name);
exit (EXIT_FAILURE);
}
/* User must specify at least one directory name on the command line. */
if (optind >= argc || argc - optind < 1)
usage (EXIT_FAILURE);
/* User must have specified some drives. */
if (drvs == NULL)
usage (EXIT_FAILURE);
/* Add drives, inspect and mount. Note that inspector is always true,
* and there is no -m option.
*/
add_drives (drvs, 'a');
if (guestfs_launch (g) == -1)
exit (EXIT_FAILURE);
inspect_mount ();
/* Free up data structures, no longer needed after this point. */
free_drives (drvs);
unsigned errors = 0;
while (optind < argc) {
const char *dir = argv[optind];
switch (mode) {
case 0: /* no -l or -R option */
if (do_ls (dir) == -1)
errors++;
break;
case MODE_LS_L: /* virt-ls -l */
if (do_ls_l (dir) == -1)
errors++;
break;
case MODE_LS_R: /* virt-ls -R */
if (do_ls_R (dir) == -1)
errors++;
break;
case MODE_LS_LR: /* virt-ls -lR */
if (do_ls_lR (dir) == -1)
errors++;
break;
default:
abort (); /* can't happen */
}
optind++;
}
guestfs_close (g);
exit (errors == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}
static int
do_ls (const char *dir)
{
char **lines;
size_t i;
if ((lines = guestfs_ls (g, dir)) == NULL) {
return -1;
}
for (i = 0; lines[i] != NULL; ++i) {
printf ("%s\n", lines[i]);
free (lines[i]);
}
free (lines);
return 0;
}
static int
do_ls_l (const char *dir)
{
char *out;
if ((out = guestfs_ll (g, dir)) == NULL)
return -1;
printf ("%s", out);
free (out);
return 0;
}
static int
do_ls_R (const char *dir)
{
/* This is TMP_TEMPLATE_ON_STACK expanded from fish.h. */
const char *tmpdir = guestfs_tmpdir ();
char tmpfile[strlen (tmpdir) + 32];
sprintf (tmpfile, "%s/virtlsXXXXXX", tmpdir);
int fd = mkstemp (tmpfile);
if (fd == -1) {
perror ("mkstemp");
exit (EXIT_FAILURE);
}
char buf[BUFSIZ]; /* also used below */
snprintf (buf, sizeof buf, "/dev/fd/%d", fd);
if (guestfs_find0 (g, dir, buf) == -1)
return -1;
if (close (fd) == -1) {
perror (tmpfile);
exit (EXIT_FAILURE);
}
/* The output of find0 is a \0-separated file. Turn each \0 into
* a \n character.
*/
fd = open (tmpfile, O_RDONLY|O_CLOEXEC);
if (fd == -1) {
perror (tmpfile);
exit (EXIT_FAILURE);
}
ssize_t r;
while ((r = read (fd, buf, sizeof buf)) > 0) {
size_t i;
for (i = 0; i < (size_t) r; ++i)
if (buf[i] == '\0')
buf[i] = '\n';
size_t n = r;
while (n > 0) {
r = write (1, buf, n);
if (r == -1) {
perror ("write");
exit (EXIT_FAILURE);
}
n -= r;
}
}
if (r == -1 || close (fd) == -1) {
perror (tmpfile);
exit (EXIT_FAILURE);
}
unlink (tmpfile);
return 0;
}
/* Adapted from
https://rwmj.wordpress.com/2010/12/15/tip-audit-virtual-machine-for-setuid-files/
*/
static char *full_path (const char *dir, const char *name);
static struct guestfs_stat_list *lstatlist (const char *dir, char **names);
static struct guestfs_xattr_list *lxattrlist (const char *dir, char **names);
static int show_file (const char *dir, const char *name, const struct guestfs_stat *stat, const struct guestfs_xattr_list *xattrs);
typedef int (*visitor_function) (const char *dir, const char *name, const struct guestfs_stat *stat, const struct guestfs_xattr_list *xattrs);
static int
visit (int depth, const char *dir, visitor_function f)
{
/* Call 'f' with the top directory. Note that ordinary recursive
* visits will not otherwise do this, so we have to have a special
* case.
*/
if (depth == 0) {
struct guestfs_stat *stat;
struct guestfs_xattr_list *xattrs;
int r;
stat = guestfs_lstat (g, dir);
if (stat == NULL)
return -1;
xattrs = guestfs_lgetxattrs (g, dir);
if (xattrs == NULL) {
guestfs_free_stat (stat);
return -1;
}
r = f (dir, NULL, stat, xattrs);
guestfs_free_stat (stat);
guestfs_free_xattr_list (xattrs);
if (r == -1)
return -1;
}
int ret = -1;
char **names = NULL;
char *path = NULL;
size_t i, xattrp;
struct guestfs_stat_list *stats = NULL;
struct guestfs_xattr_list *xattrs = NULL;
names = guestfs_ls (g, dir);
if (names == NULL)
goto out;
stats = lstatlist (dir, names);
if (stats == NULL)
goto out;
xattrs = lxattrlist (dir, names);
if (xattrs == NULL)
goto out;
/* Call function on everything in this directory. */
for (i = 0, xattrp = 0; names[i] != NULL; ++i, ++xattrp) {
struct guestfs_xattr_list file_xattrs;
size_t nr_xattrs;
assert (stats->len >= i);
assert (xattrs->len >= xattrp);
/* Find the list of extended attributes for this file. */
assert (strlen (xattrs->val[xattrp].attrname) == 0);
if (xattrs->val[xattrp].attrval_len == 0) {
fprintf (stderr, _("%s: error getting extended attrs for %s %s\n"),
program_name, dir, names[i]);
goto out;
}
/* lxattrlist function made sure attrval was \0-terminated, so we can do */
if (sscanf (xattrs->val[xattrp].attrval, "%zu", &nr_xattrs) != 1) {
fprintf (stderr, _("%s: error: cannot parse xattr count for %s %s\n"),
program_name, dir, names[i]);
goto out;
}
file_xattrs.len = nr_xattrs;
file_xattrs.val = &xattrs->val[xattrp];
xattrp += nr_xattrs;
/* Call the function. */
if (f (dir, names[i], &stats->val[i], &file_xattrs) == -1)
goto out;
/* Recursively call visit, but only on directories. */
if (is_dir (stats->val[i].mode)) {
path = full_path (dir, names[i]);
if (visit (depth + 1, path, f) == -1)
goto out;
free (path); path = NULL;
}
}
ret = 0;
out:
free (path);
if (names)
free_strings (names);
if (stats)
guestfs_free_stat_list (stats);
if (xattrs)
guestfs_free_xattr_list (xattrs);
return ret;
}
static char *
full_path (const char *dir, const char *name)
{
int r;
char *path;
if (STREQ (dir, "/"))
r = asprintf (&path, "/%s", name ? name : "");
else if (name)
r = asprintf (&path, "%s/%s", dir, name);
else
r = asprintf (&path, "%s", dir);
if (r == -1) {
perror ("asprintf");
exit (EXIT_FAILURE);
}
return path;
}
/* This calls guestfs_lstatlist, but it splits the names list up so that we
* don't overrun the libguestfs protocol limit.
*/
#define LSTATLIST_MAX 1000
static struct guestfs_stat_list *
lstatlist (const char *dir, char **names)
{
size_t len = count_strings (names);
char **first;
size_t old_len;
struct guestfs_stat_list *ret, *stats;
ret = malloc (sizeof *ret);
if (ret == NULL) {
perror ("malloc");
exit (EXIT_FAILURE);
}
ret->len = 0;
ret->val = NULL;
while (len > 0) {
first = take_strings (names, LSTATLIST_MAX, &names);
len = len <= LSTATLIST_MAX ? 0 : len - LSTATLIST_MAX;
stats = guestfs_lstatlist (g, dir, first);
/* Note we don't need to free up the strings because take_strings
* does not do a deep copy.
*/
free (first);
if (stats == NULL) {
free (ret);
return NULL;
}
/* Append stats to ret. */
old_len = ret->len;
ret->len += stats->len;
ret->val = realloc (ret->val, ret->len * sizeof (struct guestfs_stat));
if (ret->val == NULL) {
perror ("realloc");
exit (EXIT_FAILURE);
}
memcpy (&ret->val[old_len], stats->val,
stats->len * sizeof (struct guestfs_stat));
guestfs_free_stat_list (stats);
}
return ret;
}
/* Same as above, for lxattrlist. Note the rather peculiar format
* used to return the list of extended attributes (see
* guestfs_lxattrlist documentation).
*/
#define LXATTRLIST_MAX 1000
static struct guestfs_xattr_list *
lxattrlist (const char *dir, char **names)
{
size_t len = count_strings (names);
char **first;
size_t i, old_len;
struct guestfs_xattr_list *ret, *xattrs;
ret = malloc (sizeof *ret);
if (ret == NULL) {
perror ("malloc");
exit (EXIT_FAILURE);
}
ret->len = 0;
ret->val = NULL;
while (len > 0) {
first = take_strings (names, LXATTRLIST_MAX, &names);
len = len <= LXATTRLIST_MAX ? 0 : len - LXATTRLIST_MAX;
xattrs = guestfs_lxattrlist (g, dir, first);
/* Note we don't need to free up the strings because take_strings
* does not do a deep copy.
*/
free (first);
if (xattrs == NULL) {
free (ret);
return NULL;
}
/* Append xattrs to ret. */
old_len = ret->len;
ret->len += xattrs->len;
ret->val = realloc (ret->val, ret->len * sizeof (struct guestfs_xattr));
if (ret->val == NULL) {
perror ("realloc");
exit (EXIT_FAILURE);
}
for (i = 0; i < xattrs->len; ++i, ++old_len) {
/* We have to make a deep copy of the attribute name and value.
* The attrval contains 8 bit data. However make sure also that
* it is \0-terminated, because that makes the calling code
* simpler.
*/
ret->val[old_len].attrname = strdup (xattrs->val[i].attrname);
ret->val[old_len].attrval = malloc (xattrs->val[i].attrval_len + 1);
if (ret->val[old_len].attrname == NULL ||
ret->val[old_len].attrval == NULL) {
perror ("malloc");
exit (EXIT_FAILURE);
}
ret->val[old_len].attrval_len = xattrs->val[i].attrval_len;
memcpy (ret->val[old_len].attrval, xattrs->val[i].attrval,
xattrs->val[i].attrval_len);
ret->val[i].attrval[ret->val[i].attrval_len] = '\0';
}
guestfs_free_xattr_list (xattrs);
}
return ret;
}
static int
do_ls_lR (const char *dir)
{
return visit (0, dir, show_file);
}
/* This is the function which is called to display all files and
* directories, and it's where the magic happens. We are called with
* full stat and extended attributes for each file, so there is no
* penalty for displaying anything in those structures. However if we
* need other things (eg. checksum) we may have to go back to the
* appliance and then there can be a very large penalty.
*/
static int
show_file (const char *dir, const char *name,
const struct guestfs_stat *stat,
const struct guestfs_xattr_list *xattrs)
{
char filetype[2];
char *path, *csum = NULL, *link = NULL;
/* Display the basic fields. */
output_start_line ();
if (is_reg (stat->mode))
filetype[0] = '-';
else if (is_dir (stat->mode))
filetype[0] = 'd';
else if (is_chr (stat->mode))
filetype[0] = 'c';
else if (is_blk (stat->mode))
filetype[0] = 'b';
else if (is_fifo (stat->mode))
filetype[0] = 'p';
else if (is_lnk (stat->mode))
filetype[0] = 'l';
else if (is_sock (stat->mode))
filetype[0] = 's';
else
filetype[0] = 'u';
filetype[1] = '\0';
output_string (filetype);
output_int64_perms (stat->mode & 07777);
output_int64_size (stat->size);
/* Display extra fields when enabled. */
if (enable_uids) {
output_int64_uid (stat->uid);
output_int64_uid (stat->gid);
}
if (enable_times) {
output_int64_time (stat->atime);
output_int64_time (stat->mtime);
output_int64_time (stat->ctime);
}
if (enable_extra_stats) {
output_int64_dev (stat->dev);
output_int64 (stat->ino);
output_int64 (stat->nlink);
output_int64_dev (stat->rdev);
output_int64 (stat->blocks);
}
/* Disabled for now -- user would definitely want these to be interpreted.
if (enable_xattrs)
output_xattrs (xattrs);
*/
path = full_path (dir, name);
if (checksum && is_reg (stat->mode)) {
csum = guestfs_checksum (g, checksum, path);
if (!csum)
exit (EXIT_FAILURE);
output_string (csum);
}
output_string (path);
if (is_lnk (stat->mode))
/* XXX Fix this for NTFS. */
link = guestfs_readlink (g, path);
if (link)
output_string_link (link);
output_end_line ();
free (path);
free (csum);
free (link);
return 0;
}
/* Output functions.
*
* Note that we have to be careful to check return values from printf
* in these functions, because we want to catch ENOSPC errors.
*/
static int field;
static void
next_field (void)
{
int c = csv ? ',' : ' ';
field++;
if (field == 1) return;
if (putchar (c) == EOF) {
perror ("putchar");
exit (EXIT_FAILURE);
}
}
static void
output_start_line (void)
{
field = 0;
}
static void
output_end_line (void)
{
if (printf ("\n") < 0) {
perror ("printf");
exit (EXIT_FAILURE);
}
}
static void
output_string (const char *s)
{
next_field ();
if (!csv) {
print_no_quoting:
if (printf ("%s", s) < 0) {
perror ("printf");
exit (EXIT_FAILURE);
}
}
else {
/* Quote CSV string without requiring an external module. */
size_t i, len;
int needs_quoting = 0;
len = strlen (s);
for (i = 0; i < len; ++i) {
if (s[i] == ' ' || s[i] == '"' ||
s[i] == '\n' || s[i] == ',') {
needs_quoting = 1;
break;
}
}
if (!needs_quoting)
goto print_no_quoting;
/* Quoting for CSV fields. */
if (putchar ('"') == EOF) {
perror ("putchar");
exit (EXIT_FAILURE);
}
for (i = 0; i < len; ++i) {
if (s[i] == '"') {
if (putchar ('"') == EOF || putchar ('"') == EOF) {
perror ("putchar");
exit (EXIT_FAILURE);
}
} else {
if (putchar (s[i]) == EOF) {
perror ("putchar");
exit (EXIT_FAILURE);
}
}
}
if (putchar ('"') == EOF) {
perror ("putchar");
exit (EXIT_FAILURE);
}
}
}
static void
output_string_link (const char *link)
{
if (csv)
output_string (link);
else {
next_field ();
if (printf ("-> %s", link) < 0) {
perror ("printf");
exit (EXIT_FAILURE);
}
}
}
static void
output_int64 (int64_t i)
{
next_field ();
/* csv doesn't need escaping */
if (printf ("%" PRIi64, i) < 0) {
perror ("printf");
exit (EXIT_FAILURE);
}
}
static void
output_int64_size (int64_t size)
{
char buf[LONGEST_HUMAN_READABLE];
int hopts = human_round_to_nearest|human_autoscale|human_base_1024|human_SI;
int r;
next_field ();
if (!csv) {
if (!human)
r = printf ("%10" PRIi64, size);
else
r = printf ("%10s",
human_readable ((uintmax_t) size, buf, hopts, 1, 1));
} else {
/* CSV is the same as non-CSV but we don't need to right-align. */
if (!human)
r = printf ("%" PRIi64, size);
else
r = printf ("%s",
human_readable ((uintmax_t) size, buf, hopts, 1, 1));
}
if (r < 0) {
perror ("printf");
exit (EXIT_FAILURE);
}
}
static void
output_int64_perms (int64_t i)
{
next_field ();
/* csv doesn't need escaping */
if (printf ("%04" PRIo64, i) < 0) {
perror ("printf");
exit (EXIT_FAILURE);
}
}
static void
output_int64_time (int64_t i)
{
int r;
next_field ();
/* csv doesn't need escaping */
if (time_t_output) {
switch (time_relative) {
case 0: /* --time-t */
r = printf ("%10" PRIi64, i);
break;
case 1: /* --time-relative */
r = printf ("%8" PRIi64, now - i);
break;
case 2: /* --time-days */
default:
r = printf ("%3" PRIi64, (now - i) / 86400);
break;
}
}
else {
time_t t = (time_t) i;
char buf[64];
struct tm *tm;
tm = localtime (&t);
if (tm == NULL) {
perror ("localtime");
exit (EXIT_FAILURE);
}
if (strftime (buf, sizeof buf, "%F %T", tm) == 0) {
perror ("strftime");
exit (EXIT_FAILURE);
}
r = printf ("%s", buf);
}
if (r < 0) {
perror ("printf");
exit (EXIT_FAILURE);
}
}
static void
output_int64_uid (int64_t i)
{
next_field ();
/* csv doesn't need escaping */
if (printf ("%4" PRIi64, i) < 0) {
perror ("printf");
exit (EXIT_FAILURE);
}
}
static void
output_int64_dev (int64_t i)
{
dev_t dev = i;
next_field ();
/* csv doesn't need escaping */
if (printf ("%d:%d", major (dev), minor (dev)) < 0) {
perror ("printf");
exit (EXIT_FAILURE);
}
}
/* In the libguestfs API, modes returned by lstat and friends are
* defined to contain Linux ABI values. However since the "current
* operating system" might not be Linux, we have to hard-code those
* numbers here.
*/
static int
is_reg (int64_t mode)
{
return (mode & 0170000) == 0100000;
}
static int
is_dir (int64_t mode)
{
return (mode & 0170000) == 0040000;
}
static int
is_chr (int64_t mode)
{
return (mode & 0170000) == 0020000;
}
static int
is_blk (int64_t mode)
{
return (mode & 0170000) == 0060000;
}
static int
is_fifo (int64_t mode)
{
return (mode & 0170000) == 0010000;
}
/* symbolic link */
static int
is_lnk (int64_t mode)
{
return (mode & 0170000) == 0120000;
}
static int
is_sock (int64_t mode)
{
return (mode & 0170000) == 0140000;
}
/* String functions. */
static size_t
count_strings (char **names)
{
size_t ret = 0;
while (names[ret] != NULL)
ret++;
return ret;
}
static void
free_strings (char **names)
{
size_t i;
for (i = 0; names[i] != NULL; ++i)
free (names[i]);
free (names);
}
/* Take the first 'n' names, returning a newly allocated list. The
* strings themselves are not duplicated. If 'lastp' is not NULL,
* then it is updated with the pointer to the list of remaining names.
*/
static char **
take_strings (char **names, size_t n, char ***lastp)
{
size_t i;
char **ret = malloc ((n+1) * sizeof (char *));
if (ret == NULL) {
perror ("malloc");
exit (EXIT_FAILURE);
}
for (i = 0; names[i] != NULL && i < n; ++i)
ret[i] = names[i];
ret[i] = NULL;
if (lastp)
*lastp = &names[i];
return ret;
}
Jump to Line
Something went wrong with that request. Please try again.