/* Run a diff on two files. */ static void diff (struct file *file1, guestfs_h *g1, struct file *file2, guestfs_h *g2) { CLEANUP_FREE char *tmpdir = guestfs_get_tmpdir (g1); CLEANUP_FREE char *tmpd, *tmpda = NULL, *tmpdb = NULL, *cmd = NULL; int r; assert (is_reg (file1->stat->st_mode)); assert (is_reg (file2->stat->st_mode)); if (asprintf (&tmpd, "%s/virtdiffXXXXXX", tmpdir) < 0) { perror ("asprintf"); exit (EXIT_FAILURE); } if (mkdtemp (tmpd) == NULL) { perror ("mkdtemp"); exit (EXIT_FAILURE); } if (asprintf (&tmpda, "%s/a", tmpd) < 0 || asprintf (&tmpdb, "%s/b", tmpd) < 0) { perror ("asprintf"); exit (EXIT_FAILURE); } if (guestfs_download (g1, file1->path, tmpda) == -1) goto out; if (guestfs_download (g2, file2->path, tmpdb) == -1) goto out; /* Note that the tmpdir is safe, and the rest of the path * should not need quoting. */ if (asprintf (&cmd, "diff -u '%s' '%s' | tail -n +3", tmpda, tmpdb) < 0) { perror ("asprintf"); exit (EXIT_FAILURE); } if (verbose) fprintf (stderr, "%s\n", cmd); r = system (cmd); if (!WIFEXITED (r) || WEXITSTATUS (r) != 0) { fprintf (stderr, _("%s: external diff command failed\n"), guestfs_int_program_name); goto out; } printf ("@@ %s @@\n", _("End of diff")); out: unlink (tmpda); unlink (tmpdb); rmdir (tmpd); }
/* The g->tmpdir (per-handle temporary directory) is not created when * the handle is created. Instead we create it lazily before the * first time it is used, or during launch. */ int guestfs_int_lazy_make_tmpdir (guestfs_h *g) { if (!g->tmpdir) { CLEANUP_FREE char *tmpdir = guestfs_get_tmpdir (g); g->tmpdir = safe_asprintf (g, "%s/libguestfsXXXXXX", tmpdir); if (mkdtemp (g->tmpdir) == NULL) { perrorf (g, _("%s: cannot create temporary directory"), g->tmpdir); free (g->tmpdir); g->tmpdir = NULL; return -1; } } return 0; }
/* For Windows >= Vista, if evtxdump.py is installed then we can * use it to dump the System.evtx log. */ static int do_log_windows_evtx (void) { CLEANUP_FREE char *filename = NULL; CLEANUP_FREE char *tmpdir = guestfs_get_tmpdir (g); CLEANUP_UNLINK_FREE char *localfile = NULL; CLEANUP_FREE char *cmd = NULL; char dev_fd[64]; int fd, status; if (system ("evtxdump.py -h >/dev/null 2>&1") != 0) { fprintf (stderr, _("%s: you need to install 'evtxdump.py' (from the python-evtx package)\n" "in order to parse Windows Event Logs. If you cannot install this, then\n" "use virt-copy-out(1) to copy the contents of /Windows/System32/winevt/Logs\n" "from this guest, and examine in a binary file viewer.\n"), guestfs_int_program_name); return -1; } /* Check if System.evtx exists. XXX Allow the filename to be * configurable, since there are many logs. */ filename = guestfs_case_sensitive_path (g, "/Windows/System32/winevt/Logs/System.evtx"); if (filename == NULL) return -1; /* Note that guestfs_case_sensitive_path does NOT check for existence. */ if (guestfs_is_file_opts (g, filename, GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1) <= 0) { fprintf (stderr, _("%s: Windows Event Log file (%s) not found\n"), guestfs_int_program_name, filename); return -1; } /* Download the file to a temporary. Python-evtx wants to mmap * the file so we cannot use a pipe. */ if (asprintf (&localfile, "%s/virtlogXXXXXX", tmpdir) == -1) { perror ("asprintf"); return -1; } if ((fd = mkstemp (localfile)) == -1) { perror ("mkstemp"); return -1; } snprintf (dev_fd, sizeof dev_fd, "/dev/fd/%d", fd); if (guestfs_download (g, filename, dev_fd) == -1) return -1; close (fd); /* This should be safe as long as $TMPDIR is not set to something wild. */ if (asprintf (&cmd, "evtxdump.py '%s'", localfile) == -1) { perror ("asprintf"); return -1; } status = system (cmd); if (status) { char buf[256]; fprintf (stderr, "%s: %s\n", guestfs_int_program_name, guestfs_int_exit_status_to_string (status, "evtxdump.py", buf, sizeof buf)); return -1; } return 0; }
int run_edit (const char *cmd, size_t argc, char *argv[]) { CLEANUP_FREE char *tmpdir = guestfs_get_tmpdir (g); CLEANUP_UNLINK_FREE char *filename = NULL; char buf[256]; const char *editor; CLEANUP_FREE char *remotefilename = NULL, *newname = NULL; struct stat oldstat, newstat; int r, fd; if (argc != 1) { fprintf (stderr, _("use '%s filename' to edit a file\n"), cmd); return -1; } /* Choose an editor. */ if (STRCASEEQ (cmd, "vi")) editor = "vi"; else if (STRCASEEQ (cmd, "emacs")) editor = "emacs -nw"; else { editor = getenv ("EDITOR"); if (editor == NULL) editor = "vi"; /* could be cruel here and choose ed(1) */ } /* Handle 'win:...' prefix. */ remotefilename = win_prefix (argv[0]); if (remotefilename == NULL) return -1; /* Download the file and write it to a temporary. */ if (asprintf (&filename, "%s/guestfishXXXXXX", tmpdir) == -1) { perror ("asprintf"); return -1; } fd = mkstemp (filename); if (fd == -1) { perror ("mkstemp"); return -1; } snprintf (buf, sizeof buf, "/dev/fd/%d", fd); if (guestfs_download (g, remotefilename, buf) == -1) { close (fd); return -1; } if (close (fd) == -1) { perror (filename); return -1; } /* Get the old stat. */ if (stat (filename, &oldstat) == -1) { perror (filename); return -1; } /* Edit it. */ /* XXX Safe? */ snprintf (buf, sizeof buf, "%s %s", editor, filename); r = system (buf); if (r != 0) { perror (buf); return -1; } /* Get the new stat. */ if (stat (filename, &newstat) == -1) { perror (filename); return -1; } /* Changed? */ if (oldstat.st_ctime == newstat.st_ctime && oldstat.st_size == newstat.st_size) return 0; /* Upload to a new file in the same directory, so if it fails we * don't end up with a partially written file. Give the new file * a completely random name so we have only a tiny chance of * overwriting some existing file. */ newname = generate_random_name (remotefilename); if (!newname) return -1; /* Write new content. */ if (guestfs_upload (g, filename, newname) == -1) return -1; /* Set the permissions, UID, GID and SELinux context of the new * file to match the old file (RHBZ#788641). */ if (guestfs_copy_attributes (g, remotefilename, newname, GUESTFS_COPY_ATTRIBUTES_ALL, 1, -1) == -1) return -1; if (guestfs_mv (g, newname, remotefilename) == -1) return -1; return 0; }
/* Estimate the size of the input. This returns the estimated size * (in bytes) of the input. It also sets ifmt to the format of the * input, either the string "directory" if the input is a directory, * or the output of the "file" command on the input. * * Estimation is a Hard Problem. Some factors which make it hard: * * - Superblocks, block free bitmaps, FAT and other fixed overhead * - Indirect blocks (ext2, ext3), and extents * - Journal size * - Internal fragmentation of files * * What we could also do is try shrinking the filesystem after * creating and populating it, but that is complex given partitions. */ static int estimate_input (const char *input, uint64_t *estimate_rtn, char **ifmt_rtn) { struct stat statbuf; const char *argv[6]; CLEANUP_UNLINK_FREE char *tmpfile = NULL; CLEANUP_FCLOSE FILE *fp = NULL; char line[256]; size_t len; CLEANUP_FREE char *tmpdir = guestfs_get_tmpdir (g); int fd; if (asprintf (&tmpfile, "%s/makefsXXXXXX", tmpdir) == -1) { perror ("asprintf"); return -1; } fd = mkstemp (tmpfile); if (fd == -1) { perror (tmpfile); return -1; } close (fd); if (stat (input, &statbuf) == -1) { perror (input); return -1; } if (S_ISDIR (statbuf.st_mode)) { *ifmt_rtn = strdup ("directory"); if (*ifmt_rtn == NULL) { perror ("strdup"); return -1; } argv[0] = "du"; argv[1] = "--apparent-size"; argv[2] = "-b"; argv[3] = "-s"; argv[4] = input; argv[5] = NULL; if (exec_command ((char **) argv, tmpfile) == -1) return -1; fp = fopen (tmpfile, "r"); if (fp == NULL) { perror (tmpfile); return -1; } if (fgets (line, sizeof line, fp) == NULL) { perror ("fgets"); return -1; } if (sscanf (line, "%" SCNu64, estimate_rtn) != 1) { fprintf (stderr, _("%s: cannot parse the output of 'du' command: %s\n"), guestfs_int_program_name, line); return -1; } } else { argv[0] = "file"; argv[1] = "-bsLz"; argv[2] = input; argv[3] = NULL; if (exec_command ((char **) argv, tmpfile) == -1) return -1; fp = fopen (tmpfile, "r"); if (fp == NULL) { perror (tmpfile); return -1; } if (fgets (line, sizeof line, fp) == NULL) { perror ("fgets"); return -1; } len = strlen (line); if (len > 0 && line[len-1] == '\n') line[len-1] = '\0'; *ifmt_rtn = strdup (line); if (*ifmt_rtn == NULL) { perror ("strdup"); return -1; } if (strstr (line, "tar archive") == NULL) { fprintf (stderr, _("%s: %s: input is not a directory, tar archive or compressed tar archive\n"), guestfs_int_program_name, input); return -1; } if (strstr (line, "compress")) { if (strstr (line, "compress'd")) { argv[0] = "uncompress"; argv[1] = "-c"; argv[2] = input; argv[3] = NULL; } else if (strstr (line, "gzip compressed")) { argv[0] = "gzip"; argv[1] = "-cd"; argv[2] = input; argv[3] = NULL; } else if (strstr (line, "bzip2 compressed")) { argv[0] = "bzip2"; argv[1] = "-cd"; argv[2] = input; argv[3] = NULL; } else if (strstr (line, "xz compressed")) { argv[0] = "xz"; argv[1] = "-cd"; argv[2] = input; argv[3] = NULL; } else { fprintf (stderr, _("%s: %s: unknown compressed input format (%s)\n"), guestfs_int_program_name, input, line); return -1; } *estimate_rtn = 0; if (exec_command_count_output ((char **) argv, estimate_rtn) == -1) return -1; } else { /* Plain tar file, just get the size directly. Tar files have * a 512 byte block size (compared with typically 1K or 4K for * filesystems) so this isn't very accurate. */ *estimate_rtn = statbuf.st_size; } } return 0; }
int run_more (const char *cmd, size_t argc, char *argv[]) { CLEANUP_FREE char *tmpdir = guestfs_get_tmpdir (g); CLEANUP_UNLINK_FREE char *filename = NULL; char buf[256]; CLEANUP_FREE char *remote = NULL; const char *pager; int r, fd; if (argc != 1) { fprintf (stderr, _("use '%s filename' to page a file\n"), cmd); return -1; } /* Choose a pager. */ if (STRCASEEQ (cmd, "less")) pager = "less"; else { pager = getenv ("PAGER"); if (pager == NULL) pager = "more"; } /* Allow win:... prefix on remote. */ remote = win_prefix (argv[0]); if (remote == NULL) return -1; /* Download the file and write it to a temporary. */ if (asprintf (&filename, "%s/guestfishXXXXXX", tmpdir) == -1) { perror ("asprintf"); return -1; } fd = mkstemp (filename); if (fd == -1) { perror ("mkstemp"); return -1; } snprintf (buf, sizeof buf, "/dev/fd/%d", fd); if (guestfs_download (g, remote, buf) == -1) { close (fd); return -1; } if (close (fd) == -1) { perror (filename); return -1; } /* View it. */ /* XXX Safe? */ snprintf (buf, sizeof buf, "%s %s", pager, filename); r = system (buf); if (r != 0) { perror (buf); return -1; } return 0; }
int run_hexedit (const char *cmd, size_t argc, char *argv[]) { if (argc < 1 || argc > 3) { fprintf (stderr, _("hexedit (device|filename) [max | start max]\n")); return -1; } const char *filename = argv[0]; off_t size = get_size (filename); if (size == -1) return -1; if (size == 0) { fprintf (stderr, _("hexedit: %s is a zero length file or device\n"), filename); return -1; } off_t start; off_t max; if (argc == 1) { /* hexedit device */ /* Check we're not going to download a huge file. */ if (size > MAX_DOWNLOAD_SIZE) { fprintf (stderr, _("hexedit: %s is larger than %s. You must supply a limit using\n" " 'hexedit %s <max>' (eg. 'hexedit %s 1M') or a range using\n" " 'hexedit %s <start> <max>'.\n"), filename, MAX_DOWNLOAD_SIZE_TEXT, filename, filename, filename); return -1; } start = 0; max = size; } else { if (argc == 3) { /* hexedit device start max */ if (parse_size (argv[1], &start) == -1) return -1; if (parse_size (argv[2], &max) == -1) return -1; } else { /* hexedit device max */ start = 0; if (parse_size (argv[1], &max) == -1) return -1; } if (start + max > size) max = size - start; } if (max <= 0) { fprintf (stderr, _("hexedit: invalid range\n")); return -1; } /* Download the requested range from the remote file|device into a * local temporary file. */ const char *editor; int r; struct stat oldstat, newstat; char tmpfd[sizeof "/dev/fd/" + 3 * sizeof (int)]; CLEANUP_FREE char *editcmd = NULL; CLEANUP_FREE char *tmpdir = guestfs_get_tmpdir (g), *tmp = NULL; if (asprintf (&tmp, "%s/guestfishXXXXXX", tmpdir) == -1) { perror ("asprintf"); return -1; } int fd = mkstemp (tmp); if (fd == -1) { perror ("mkstemp"); return -1; } /* Choose an editor. */ editor = getenv ("HEXEDITOR"); if (editor == NULL) editor = "hexedit"; snprintf (tmpfd, sizeof tmpfd, "/dev/fd/%d", fd); if (guestfs_download_offset (g, filename, tmpfd, start, max) == -1) { unlink (tmp); close (fd); return -1; } if (close (fd) == -1) { unlink (tmp); return -1; } /* Get the old stat. */ if (stat (tmp, &oldstat) == -1) { perror (tmp); unlink (tmp); return -1; } /* Edit it. */ if (asprintf (&editcmd, "%s %s", editor, tmp) == -1) { perror ("asprintf"); return -1; } r = system (editcmd); if (r != 0) { perror (editcmd); unlink (tmp); return -1; } /* Get the new stat. */ if (stat (tmp, &newstat) == -1) { perror (tmp); unlink (tmp); return -1; } /* Changed? */ if (oldstat.st_ctime == newstat.st_ctime && oldstat.st_size == newstat.st_size) { unlink (tmp); return 0; } /* Write new content. */ if (guestfs_upload_offset (g, tmp, filename, start) == -1) { unlink (tmp); return -1; } unlink (tmp); return 0; }
int run_display (const char *cmd, size_t argc, char *argv[]) { CLEANUP_FREE char *tmpdir = guestfs_get_tmpdir (g), *filename = NULL; CLEANUP_FREE char *remote = NULL; const char *display; char buf[256]; int r, fd; if (argc != 1) { fprintf (stderr, _("display filename\n")); return -1; } /* Choose a display command. */ display = getenv ("GUESTFISH_DISPLAY_IMAGE"); if (display == NULL) display = "display"; /* Allow win:... prefix on remote. */ remote = win_prefix (argv[0]); if (remote == NULL) return -1; /* Download the file and write it to a temporary. */ if (asprintf (&filename, "%s/guestfishXXXXXX", tmpdir) == -1) { perror ("asprintf"); return -1; } fd = mkstemp (filename); if (fd == -1) { perror ("mkstemp"); return -1; } snprintf (buf, sizeof buf, "/dev/fd/%d", fd); if (guestfs_download (g, remote, buf) == -1) { close (fd); unlink (filename); return -1; } if (close (fd) == -1) { perror (filename); unlink (filename); return -1; } /* View it. */ snprintf (buf, sizeof buf, "%s %s", display, filename); r = system (buf); unlink (filename); if (r != 0) { perror (buf); return -1; } return 0; }
static void edit (const char *filename, const char *root) { CLEANUP_FREE char *filename_to_free = NULL; CLEANUP_FREE char *tmpdir = guestfs_get_tmpdir (g); char tmpfile[strlen (tmpdir) + 32]; sprintf (tmpfile, "%s/virteditXXXXXX", tmpdir); int fd; char fdbuf[32]; CLEANUP_FREE char *upload_from = NULL; CLEANUP_FREE char *newname = NULL; CLEANUP_FREE char *backupname = NULL; /* Windows? Special handling is required. */ if (is_windows (g, root)) filename = filename_to_free = windows_path (g, root, filename); /* Download the file to a temporary. */ fd = mkstemp (tmpfile); if (fd == -1) { perror ("mkstemp"); exit (EXIT_FAILURE); } snprintf (fdbuf, sizeof fdbuf, "/dev/fd/%d", fd); if (guestfs_download (g, filename, fdbuf) == -1) goto error; if (close (fd) == -1) { perror (tmpfile); goto error; } if (!perl_expr) upload_from = edit_interactively (tmpfile); else upload_from = edit_non_interactively (tmpfile); /* We don't always need to upload: upload_from could be NULL because * the user closed the editor without changing the file. */ if (upload_from) { /* Upload to a new file in the same directory, so if it fails we * don't end up with a partially written file. Give the new file * a completely random name so we have only a tiny chance of * overwriting some existing file. */ newname = generate_random_name (filename); if (guestfs_upload (g, upload_from, newname) == -1) goto error; /* Set the permissions, UID, GID and SELinux context of the new * file to match the old file (RHBZ#788641). */ if (copy_attributes (filename, newname) == -1) goto error; /* Backup or overwrite the file. */ if (backup_extension) { backupname = generate_backup_name (filename); if (guestfs_mv (g, filename, backupname) == -1) goto error; } if (guestfs_mv (g, newname, filename) == -1) goto error; } unlink (tmpfile); return; error: unlink (tmpfile); exit (EXIT_FAILURE); }
static char * cpio_arch (guestfs_h *g, const char *file, const char *path) { CLEANUP_FREE char *tmpdir = guestfs_get_tmpdir (g), *dir = NULL; CLEANUP_FREE char *initrd = NULL; CLEANUP_CMD_CLOSE struct command *cmd = guestfs___new_command (g); char *ret = NULL; const char *method; int64_t size; int r; size_t i; if (asprintf (&dir, "%s/libguestfsXXXXXX", tmpdir) == -1) { perror ("asprintf"); return NULL; } if (strstr (file, "gzip")) method = "zcat"; else if (strstr (file, "bzip2")) method = "bzcat"; else method = "cat"; /* Security: Refuse to download initrd if it is huge. */ size = guestfs_filesize (g, path); if (size == -1 || size > 100000000) { error (g, _("size of %s unreasonable (%" PRIi64 " bytes)"), path, size); goto out; } if (mkdtemp (dir) == NULL) { perrorf (g, "mkdtemp"); goto out; } initrd = safe_asprintf (g, "%s/initrd", dir); if (guestfs_download (g, path, initrd) == -1) goto out; /* Construct a command to extract named binaries from the initrd file. */ guestfs___cmd_add_string_unquoted (cmd, "cd "); guestfs___cmd_add_string_quoted (cmd, dir); guestfs___cmd_add_string_unquoted (cmd, " && "); guestfs___cmd_add_string_unquoted (cmd, method); guestfs___cmd_add_string_unquoted (cmd, " initrd | cpio --quiet -id"); for (i = 0; initrd_binaries[i] != NULL; ++i) { guestfs___cmd_add_string_unquoted (cmd, " "); guestfs___cmd_add_string_quoted (cmd, initrd_binaries[i]); } r = guestfs___cmd_run (cmd); if (r == -1) goto out; if (!WIFEXITED (r) || WEXITSTATUS (r) != 0) { guestfs___external_command_failed (g, r, "cpio", path); goto out; } for (i = 0; initrd_binaries[i] != NULL; ++i) { CLEANUP_FREE char *bin = safe_asprintf (g, "%s/%s", dir, initrd_binaries[i]); if (is_regular_file (bin)) { int flags = g->verbose ? MAGIC_DEBUG : 0; flags |= MAGIC_ERROR | MAGIC_RAW; magic_t m = magic_open (flags); if (m == NULL) { perrorf (g, "magic_open"); goto out; } if (magic_load (m, NULL) == -1) { perrorf (g, "magic_load: default magic database file"); magic_close (m); goto out; } const char *line = magic_file (m, bin); if (line == NULL) { perrorf (g, "magic_file: %s", bin); magic_close (m); goto out; } CLEANUP_FREE char *elf_arch = match1 (g, line, re_file_elf); if (elf_arch != NULL) { ret = canonical_elf_arch (g, elf_arch); magic_close (m); goto out; } magic_close (m); } } error (g, "file_architecture: could not determine architecture of cpio archive"); out: guestfs___recursive_remove_dir (g, dir); return ret; }