/* Extract the information from the entry, serialize and send it out. * Return 0 on success, -1 on error. */ static int send_dirent_info (TSK_FS_FILE *fsfile, const char *path) { XDR xdr; int ret = 0; size_t len = 0; struct guestfs_int_tsk_dirent dirent; CLEANUP_FREE char *buf = NULL, *fname = NULL; /* Set dirent fields */ memset (&dirent, 0, sizeof dirent); /* Build the full relative path of the entry */ ret = asprintf (&fname, "%s%s", path, fsfile->name->name); if (ret < 0) { perror ("asprintf"); return -1; } dirent.tsk_inode = fsfile->name->meta_addr; dirent.tsk_type = file_type (fsfile); dirent.tsk_name = fname; dirent.tsk_flags = file_flags (fsfile); file_metadata (fsfile->meta, &dirent); /* Serialize tsk_dirent struct. */ buf = malloc (GUESTFS_MAX_CHUNK_SIZE); if (buf == NULL) { perror ("malloc"); return -1; } xdrmem_create (&xdr, buf, GUESTFS_MAX_CHUNK_SIZE, XDR_ENCODE); ret = xdr_guestfs_int_tsk_dirent (&xdr, &dirent); if (ret == 0) { perror ("xdr_guestfs_int_tsk_dirent"); return -1; } len = xdr_getpos (&xdr); xdr_destroy (&xdr); /* Send serialised tsk_dirent out. */ return send_file_write (buf, len); }
/* Has one FileOut parameter. */ int do_find0 (const char *dir) { struct stat statbuf; int r; FILE *fp; char *cmd; char *sysrootdir; size_t sysrootdirlen, len; char str[GUESTFS_MAX_CHUNK_SIZE]; sysrootdir = sysroot_path (dir); if (!sysrootdir) { reply_with_perror ("malloc"); return -1; } r = stat (sysrootdir, &statbuf); if (r == -1) { reply_with_perror ("%s", dir); free (sysrootdir); return -1; } if (!S_ISDIR (statbuf.st_mode)) { reply_with_error ("%s: not a directory", dir); free (sysrootdir); return -1; } sysrootdirlen = strlen (sysrootdir); if (asprintf_nowarn (&cmd, "%s %Q -print0", str_find, sysrootdir) == -1) { reply_with_perror ("asprintf"); free (sysrootdir); return -1; } free (sysrootdir); if (verbose) fprintf (stderr, "%s\n", cmd); fp = popen (cmd, "r"); if (fp == NULL) { reply_with_perror ("%s", cmd); free (cmd); return -1; } free (cmd); /* Now we must send the reply message, before the file contents. After * this there is no opportunity in the protocol to send any error * message back. Instead we can only cancel the transfer. */ reply (NULL, NULL); while ((r = input_to_nul (fp, str, GUESTFS_MAX_CHUNK_SIZE)) > 0) { len = strlen (str); if (len <= sysrootdirlen) continue; /* Remove the directory part of the path before sending it. */ if (send_file_write (str + sysrootdirlen, r - sysrootdirlen) < 0) { pclose (fp); return -1; } } if (ferror (fp)) { perror (dir); send_file_end (1); /* Cancel. */ pclose (fp); return -1; } if (pclose (fp) != 0) { perror (dir); send_file_end (1); /* Cancel. */ return -1; } if (send_file_end (0)) /* Normal end of file. */ return -1; return 0; }
/* Has one FileOut parameter. */ int do_checksums_out (const char *csumtype, const char *dir) { struct stat statbuf; int r; const char *program = program_of_csum (csumtype); if (program == NULL) return -1; CLEANUP_FREE char *sysrootdir = sysroot_path (dir); if (!sysrootdir) { reply_with_perror ("malloc"); return -1; } r = stat (sysrootdir, &statbuf); if (r == -1) { reply_with_perror ("%s", dir); return -1; } if (!S_ISDIR (statbuf.st_mode)) { reply_with_error ("%s: not a directory", dir); return -1; } CLEANUP_FREE char *cmd = NULL; if (asprintf_nowarn (&cmd, "cd %Q && %s -type f -print0 | %s -0 %s", sysrootdir, str_find, str_xargs, program) == -1) { reply_with_perror ("asprintf"); return -1; } if (verbose) fprintf (stderr, "%s\n", cmd); FILE *fp = popen (cmd, "r"); if (fp == NULL) { reply_with_perror ("%s", cmd); return -1; } /* Now we must send the reply message, before the file contents. After * this there is no opportunity in the protocol to send any error * message back. Instead we can only cancel the transfer. */ reply (NULL, NULL); char str[GUESTFS_MAX_CHUNK_SIZE]; while ((r = fread (str, 1, GUESTFS_MAX_CHUNK_SIZE, fp)) > 0) { if (send_file_write (str, r) < 0) { pclose (fp); return -1; } } if (ferror (fp)) { fprintf (stderr, "fread: %s: %m\n", dir); send_file_end (1); /* Cancel. */ pclose (fp); return -1; } if (pclose (fp) != 0) { fprintf (stderr, "pclose: %s: %m\n", dir); send_file_end (1); /* Cancel. */ return -1; } if (send_file_end (0)) /* Normal end of file. */ return -1; return 0; }
/* Takes optional arguments, consult optargs_bitmask. */ int do_tar_out (const char *dir, const char *compress, int numericowner, char *const *excludes) { CLEANUP_FREE char *buf = NULL; struct stat statbuf; const char *filter; int r; FILE *fp; CLEANUP_FREE char *excludes_args = NULL; CLEANUP_FREE char *cmd = NULL; char buffer[GUESTFS_MAX_CHUNK_SIZE]; if ((optargs_bitmask & GUESTFS_TAR_OUT_COMPRESS_BITMASK)) { if (STREQ (compress, "compress")) filter = " --compress"; else if (STREQ (compress, "gzip")) filter = " --gzip"; else if (STREQ (compress, "bzip2")) filter = " --bzip2"; else if (STREQ (compress, "xz")) filter = " --xz"; else if (STREQ (compress, "lzop")) filter = " --lzop"; else { reply_with_error ("unknown compression type: %s", compress); return -1; } } else filter = ""; if (!(optargs_bitmask & GUESTFS_TAR_OUT_NUMERICOWNER_BITMASK)) numericowner = 0; if ((optargs_bitmask & GUESTFS_TAR_OUT_EXCLUDES_BITMASK)) { excludes_args = make_excludes_args (excludes); if (!excludes_args) return -1; } else { excludes_args = strdup (""); if (excludes_args == NULL) { reply_with_perror ("strdup"); return -1; } } /* Check the filename exists and is a directory (RHBZ#908322). */ buf = sysroot_path (dir); if (buf == NULL) { reply_with_perror ("malloc"); return -1; } if (stat (buf, &statbuf) == -1) { reply_with_perror ("stat: %s", dir); return -1; } if (! S_ISDIR (statbuf.st_mode)) { reply_with_error ("%s: not a directory", dir); return -1; } /* "tar -C /sysroot%s -cf - ." but we have to quote the dir. */ if (asprintf_nowarn (&cmd, "%s -C %s%s%s%s -cf - .", str_tar, buf, filter, numericowner ? " --numeric-owner" : "", excludes_args) == -1) { reply_with_perror ("asprintf"); return -1; } if (verbose) fprintf (stderr, "%s\n", cmd); fp = popen (cmd, "r"); if (fp == NULL) { reply_with_perror ("%s", cmd); return -1; } /* Now we must send the reply message, before the file contents. After * this there is no opportunity in the protocol to send any error * message back. Instead we can only cancel the transfer. */ reply (NULL, NULL); while ((r = fread (buffer, 1, sizeof buffer, fp)) > 0) { if (send_file_write (buffer, r) < 0) { pclose (fp); return -1; } } if (ferror (fp)) { fprintf (stderr, "fread: %s: %m\n", dir); send_file_end (1); /* Cancel. */ pclose (fp); return -1; } if (pclose (fp) != 0) { fprintf (stderr, "pclose: %s: %m\n", dir); send_file_end (1); /* Cancel. */ return -1; } if (send_file_end (0)) /* Normal end of file. */ return -1; return 0; }
/* Has one FileOut parameter. */ int do_find0 (const char *dir) { struct stat statbuf; int r; FILE *fp; CLEANUP_FREE char *cmd = NULL; CLEANUP_FREE char *sysrootdir = NULL; size_t sysrootdirlen; CLEANUP_FREE char *str = NULL; str = malloc (GUESTFS_MAX_CHUNK_SIZE); if (str == NULL) { reply_with_perror ("malloc"); return -1; } sysrootdir = sysroot_path (dir); if (!sysrootdir) { reply_with_perror ("malloc"); return -1; } r = stat (sysrootdir, &statbuf); if (r == -1) { reply_with_perror ("%s", dir); return -1; } if (!S_ISDIR (statbuf.st_mode)) { reply_with_error ("%s: not a directory", dir); return -1; } sysrootdirlen = strlen (sysrootdir); if (asprintf_nowarn (&cmd, "%s %Q -print0", str_find, sysrootdir) == -1) { reply_with_perror ("asprintf"); return -1; } if (verbose) fprintf (stderr, "%s\n", cmd); fp = popen (cmd, "r"); if (fp == NULL) { reply_with_perror ("%s", cmd); return -1; } /* Now we must send the reply message, before the file contents. After * this there is no opportunity in the protocol to send any error * message back. Instead we can only cancel the transfer. */ reply (NULL, NULL); /* The code below assumes each path returned can fit into a protocol * chunk (if not you'll get a runtime protocol error). If this * turns out not to be a problem at some point in the future then * we'll need to modify the code to handle it. XXX */ while ((r = input_to_nul (fp, str, GUESTFS_MAX_CHUNK_SIZE)) > 0) { size_t len = strlen (str); if (len <= sysrootdirlen) continue; /* Remove the directory part of the path before sending it. */ if (send_file_write (str + sysrootdirlen, r - sysrootdirlen) < 0) { pclose (fp); return -1; } } if (ferror (fp)) { fprintf (stderr, "fgetc: %s: %m\n", dir); send_file_end (1); /* Cancel. */ pclose (fp); return -1; } if (pclose (fp) != 0) { fprintf (stderr, "pclose: %s: %m\n", dir); send_file_end (1); /* Cancel. */ return -1; } if (send_file_end (0)) /* Normal end of file. */ return -1; return 0; }
/* Has one FileOut parameter. */ static int do_compressX_out (const char *file, const char *filter, int is_device) { int r; FILE *fp; CLEANUP_FREE char *cmd = NULL; char buf[GUESTFS_MAX_CHUNK_SIZE]; /* The command will look something like: * gzip -c /sysroot%s # file * or: * gzip -c < %s # device * * We have to quote the file or device name. * * The unnecessary redirect for devices is there because lzop * unhelpfully refuses to compress anything that isn't a regular * file. */ if (!is_device) { if (asprintf_nowarn (&cmd, "%s %R", filter, file) == -1) { reply_with_perror ("asprintf"); return -1; } } else { if (asprintf_nowarn (&cmd, "%s < %Q", filter, file) == -1) { reply_with_perror ("asprintf"); return -1; } } if (verbose) fprintf (stderr, "%s\n", cmd); fp = popen (cmd, "r"); if (fp == NULL) { reply_with_perror ("%s", cmd); return -1; } /* Now we must send the reply message, before the file contents. After * this there is no opportunity in the protocol to send any error * message back. Instead we can only cancel the transfer. */ reply (NULL, NULL); while ((r = fread (buf, 1, sizeof buf, fp)) > 0) { if (send_file_write (buf, r) < 0) { pclose (fp); return -1; } } if (ferror (fp)) { fprintf (stderr, "fread: %s: %m\n", file); send_file_end (1); /* Cancel. */ pclose (fp); return -1; } if (pclose (fp) != 0) { fprintf (stderr, "pclose: %s: %m\n", file); send_file_end (1); /* Cancel. */ return -1; } if (send_file_end (0)) /* Normal end of file. */ return -1; return 0; }
/* Has one FileOut parameter. */ int do_base64_out (const char *file) { CLEANUP_FREE char *buf = NULL; struct stat statbuf; int r; FILE *fp; CLEANUP_FREE char *cmd = NULL; char buffer[GUESTFS_MAX_CHUNK_SIZE]; /* Check the filename exists and is not a directory (RHBZ#908322). */ buf = sysroot_path (file); if (buf == NULL) { reply_with_perror ("malloc"); return -1; } if (stat (buf, &statbuf) == -1) { reply_with_perror ("stat: %s", file); return -1; } if (S_ISDIR (statbuf.st_mode)) { reply_with_error ("%s: is a directory", file); return -1; } /* Construct the command. */ if (asprintf_nowarn (&cmd, "%s %Q", str_base64, buf) == -1) { reply_with_perror ("asprintf"); return -1; } if (verbose) fprintf (stderr, "%s\n", cmd); fp = popen (cmd, "r"); if (fp == NULL) { reply_with_perror ("%s", cmd); return -1; } /* Now we must send the reply message, before the file contents. After * this there is no opportunity in the protocol to send any error * message back. Instead we can only cancel the transfer. */ reply (NULL, NULL); while ((r = fread (buffer, 1, sizeof buffer, fp)) > 0) { if (send_file_write (buffer, r) < 0) { pclose (fp); return -1; } } if (ferror (fp)) { fprintf (stderr, "fread: %s: %m\n", file); send_file_end (1); /* Cancel. */ pclose (fp); return -1; } if (pclose (fp) != 0) { fprintf (stderr, "pclose: %s: %m\n", file); send_file_end (1); /* Cancel. */ return -1; } if (send_file_end (0)) /* Normal end of file. */ return -1; return 0; }
/* Takes optional arguments, consult optargs_bitmask. */ int do_ntfsclone_out (const char *device, int metadataonly, int rescue, int ignorefscheck, int preservetimestamps, int force) { int r; FILE *fp; char *cmd; char buf[GUESTFS_MAX_CHUNK_SIZE]; /* Construct the ntfsclone command. */ if (asprintf (&cmd, "ntfsclone -o - --save-image%s%s%s%s%s %s", (optargs_bitmask & GUESTFS_NTFSCLONE_OUT_METADATAONLY_BITMASK) && metadataonly ? " --metadata" : "", (optargs_bitmask & GUESTFS_NTFSCLONE_OUT_RESCUE_BITMASK) && rescue ? " --rescue" : "", (optargs_bitmask & GUESTFS_NTFSCLONE_OUT_IGNOREFSCHECK_BITMASK) && ignorefscheck ? " --ignore-fs-check" : "", (optargs_bitmask & GUESTFS_NTFSCLONE_OUT_PRESERVETIMESTAMPS_BITMASK) && preservetimestamps ? " --preserve-timestamps" : "", (optargs_bitmask & GUESTFS_NTFSCLONE_OUT_FORCE_BITMASK) && force ? " --force" : "", device) == -1) { reply_with_perror ("asprintf"); return -1; } if (verbose) fprintf (stderr, "%s\n", cmd); fp = popen (cmd, "r"); if (fp == NULL) { reply_with_perror ("%s", cmd); free (cmd); return -1; } free (cmd); /* Now we must send the reply message, before the file contents. After * this there is no opportunity in the protocol to send any error * message back. Instead we can only cancel the transfer. */ reply (NULL, NULL); while ((r = fread (buf, 1, sizeof buf, fp)) > 0) { if (send_file_write (buf, r) < 0) { pclose (fp); return -1; } } if (ferror (fp)) { perror (device); send_file_end (1); /* Cancel. */ pclose (fp); return -1; } if (pclose (fp) != 0) { perror (device); send_file_end (1); /* Cancel. */ return -1; } if (send_file_end (0)) /* Normal end of file. */ return -1; return 0; }
/* Has one FileOut parameter. */ int do_ls0 (const char *path) { DIR *dir; struct dirent *d; size_t len; CHROOT_IN; dir = opendir (path); CHROOT_OUT; if (dir == NULL) { reply_with_perror ("opendir: %s", path); return -1; } /* Now we must send the reply message, before the filenames. After * this there is no opportunity in the protocol to send any error * message back. Instead we can only cancel the transfer. */ reply (NULL, NULL); while (1) { errno = 0; d = readdir (dir); if (d == NULL) break; /* Ignore . and .. */ if (STREQ (d->d_name, ".") || STREQ (d->d_name, "..")) continue; /* Send the name in a single chunk. XXX Needs to be fixed if * names can be longer than the chunk size. Note we use 'len+1' * because we want to include the \0 terminating character in the * output. */ len = strlen (d->d_name); if (send_file_write (d->d_name, len+1) < 0) { closedir (dir); return -1; } } if (errno != 0) { fprintf (stderr, "readdir: %s: %m\n", path); send_file_end (1); /* Cancel. */ closedir (dir); return -1; } if (closedir (dir) == -1) { fprintf (stderr, "closedir: %s: %m\n", path); send_file_end (1); /* Cancel. */ return -1; } if (send_file_end (0)) /* Normal end of file. */ return -1; return 0; }