static int write_or_append (guestfs_h *g, const char *path, const char *content, size_t size, int append) { CLEANUP_UNLINK_FREE char *tmpfile = NULL; int fd = -1; int64_t filesize; /* If the content is small enough, use guestfs_internal_write{,_append} * since that call is more efficient. */ if (size <= 2*1024*1024) return (!append ? guestfs_internal_write : guestfs_internal_write_append) (g, path, content, size); if (guestfs_int_lazy_make_tmpdir (g) == -1) goto err; /* Write the content out to a temporary file. */ tmpfile = safe_asprintf (g, "%s/write%d", g->tmpdir, ++g->unique); fd = open (tmpfile, O_WRONLY|O_CREAT|O_NOCTTY|O_CLOEXEC, 0600); if (fd == -1) { perrorf (g, "open: %s", tmpfile); goto err; } if (full_write (fd, content, size) != size) { perrorf (g, "write: %s", tmpfile); goto err; } if (close (fd) == -1) { perrorf (g, "close: %s", tmpfile); goto err; } fd = -1; if (!append) { if (guestfs_upload (g, tmpfile, path) == -1) goto err; } else { /* XXX Should have an 'upload-append' call to make this atomic. */ filesize = guestfs_filesize (g, path); if (filesize == -1) goto err; if (guestfs_upload_offset (g, tmpfile, path, filesize) == -1) goto err; } return 0; err: if (fd >= 0) close (fd); return -1; }
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; }