diff options
Diffstat (limited to 'fish/hexedit.c')
-rw-r--r-- | fish/hexedit.c | 190 |
1 files changed, 190 insertions, 0 deletions
diff --git a/fish/hexedit.c b/fish/hexedit.c new file mode 100644 index 00000000..dfa18890 --- /dev/null +++ b/fish/hexedit.c @@ -0,0 +1,190 @@ +/* guestfish - the filesystem interactive shell + * Copyright (C) 2010 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <inttypes.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include "fish.h" + +#define MAX_DOWNLOAD_SIZE (16 * 1024 * 1024) +#define MAX_DOWNLOAD_SIZE_TEXT "16MB" + +static off_t get_size (const char *filename); + +int +run_hexedit (const char *cmd, int 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 buf[BUFSIZ]; + char tmp[] = "/tmp/guestfishXXXXXX"; + int fd = mkstemp (tmp); + if (fd == -1) { + perror ("mkstemp"); + return -1; + } + + /* Choose an editor. */ + editor = getenv ("HEXEDITOR"); + if (editor == NULL) + editor = "hexedit"; + + snprintf (buf, sizeof buf, "/dev/fd/%d", fd); + + if (guestfs_download_offset (g, filename, buf, 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. */ + snprintf (buf, sizeof buf, "%s %s", editor, tmp); + + r = system (buf); + if (r != 0) { + perror (buf); + 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; +} + +/* Get the size of the file or block device. */ +static off_t +get_size (const char *filename) +{ + int64_t size; + + if (STRPREFIX (filename, "/dev/")) { + size = guestfs_blockdev_getsize64 (g, filename); + if (size == -1) + return -1; + } + else { + size = guestfs_filesize (g, filename); + if (size == -1) + return -1; + } + + /* This case should be safe because we always compile with + * 64 bit file offsets. + */ + return (off_t) size; +} |