Exemple #1
0
/* 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);
}
Exemple #2
0
/* 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;
}
Exemple #3
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;
}
Exemple #4
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;
}
Exemple #5
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;
}
Exemple #6
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;
}
Exemple #7
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;
}
Exemple #8
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;
}
Exemple #9
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);
}
Exemple #10
0
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;
}