/* mount.cc Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2005, 2008, 2009 Red Hat, Inc. This file is part of Cygwin. This software is a copyrighted work licensed under the terms of the Cygwin license. Please consult the file "CYGWIN_LICENSE" for details. */ #include #include #include #include #include #include #include #include #include #include #include "path.h" #ifdef errno #undef errno #endif #include #define NT_MAX_PATH 32768 #define EXEC_FLAGS (MOUNT_EXEC | MOUNT_NOTEXEC | MOUNT_CYGWIN_EXEC) static void mount_entries (void); static void show_mounts (void); static void show_cygdrive_info (void); static void change_cygdrive_prefix (const char *new_prefix, int flags); static int mount_already_exists (const char *posix_path, int flags); // static short create_missing_dirs = FALSE; static bool force = false; static const char version[] = "$Revision$"; static const char *progname; static void error (const char *path) { fprintf (stderr, "%s: %s: %s\n", progname, path, (errno == EMFILE) ? "Too many mount entries" : strerror (errno)); exit (1); } /* FIXME: do_mount should also print a warning message if the dev arg is a non-existent Win32 path. */ static void do_mount (const char *dev, const char *where, int flags) { struct stat statbuf; int statres; statres = stat (where, &statbuf); #if 0 if (statres == -1) { /* FIXME: this'll fail if mount dir is missing any parent dirs */ if (create_missing_dirs == TRUE) { if (mkdir (where, 0755) == -1) fprintf (stderr, "Warning: unable to create %s!\n", where); else statres = 0; /* Pretend stat succeeded if we could mkdir. */ } } #endif if (statres == -1) { if (!force) fprintf (stderr, "%s: warning - %s does not exist.\n", progname, where); } else if (!(statbuf.st_mode & S_IFDIR)) { if (!force) fprintf (stderr, "%s: warning: %s is not a directory.\n", progname, where); } if (!force && !(flags & EXEC_FLAGS) && strlen (dev)) { char devtmp[1 + 2 * strlen (dev)]; strcpy (devtmp, dev); char c = strchr (devtmp, '\0')[-1]; if (c == '/' || c == '\\') strcat (devtmp, "."); /* Use a curious property of Windows which allows the use of \.. even on non-directory paths. */ for (const char *p = dev; (p = strpbrk (p, "/\\")); p++) strcat (devtmp, "\\.."); strcat (devtmp, "\\"); if (GetDriveType (devtmp) == DRIVE_REMOTE) { fprintf (stderr, "%s: defaulting to 'notexec' mount option for speed since native path\n" "%*creferences a remote share. Use '-f' option to override.\n", progname, strlen(progname) + 2, ' '); flags |= MOUNT_NOTEXEC; } } if (mount (dev, where, flags)) error (where); exit (0); } static void from_fstab (bool user) { char path[PATH_MAX]; char buf[65536]; mnt_t *m = mount_table + max_mount_entry; strcpy (path, "/etc/fstab"); if (user) { strcat (path, ".d/"); strcat (path, getlogin ()); } FILE *fh = fopen (path, "rt"); if (!fh) return; while (fgets (buf, 65536, fh)) { char *c = strrchr (buf, '\n'); if (*c) *c = '\0'; if (from_fstab_line (m, buf, user)) ++m; } max_mount_entry = m - mount_table; fclose (fh); } static void do_mount_from_fstab (const char *where) { force = true; /* Read fstab entries. */ from_fstab (false); from_fstab (true); /* Loop through fstab entries and see if it matches `where'. If `where' is NULL, all entries match. */ bool exists = false; for (mnt_t *m = mount_table; m - mount_table < max_mount_entry; ++m) if (!(m->flags & MOUNT_CYGDRIVE) && (!where || !strcmp (where, m->posix))) { exists = true; /* Compare with existing mount table. If the entry doesn't exist, mount it. */ FILE *mt = setmntent ("/-not-used-", "r"); struct mntent *p; while ((p = getmntent (mt)) != NULL) if (!strcmp (m->posix, p->mnt_dir)) break; if (!p) do_mount (m->native, m->posix, m->flags); endmntent (mt); if (where) break; } if (!exists && where) fprintf (stderr, "%s: can't find %s in /etc/fstab or in /etc/fstab.d/$USER\n", progname, where); } static struct option longopts[] = { {"all", no_argument, NULL, 'a'}, {"change-cygdrive-prefix", no_argument, NULL, 'c'}, {"force", no_argument, NULL, 'f'}, {"help", no_argument, NULL, 'h' }, {"mount-entries", no_argument, NULL, 'm'}, {"options", required_argument, NULL, 'o'}, {"show-cygdrive-prefix", no_argument, NULL, 'p'}, {"version", no_argument, NULL, 'v'}, {NULL, 0, NULL, 0} }; static char opts[] = "acfhmpvo:"; struct opt { const char *name; unsigned val; bool clear; } oopts[] = { {"acl", MOUNT_NOACL, true}, {"auto", 0, false}, {"binary", MOUNT_BINARY, false}, {"cygexec", MOUNT_CYGWIN_EXEC, false}, {"exec", MOUNT_EXEC, false}, {"noacl", MOUNT_NOACL, false}, {"nosuid", 0, false}, {"notexec", MOUNT_NOTEXEC, false}, {"override", MOUNT_OVERRIDE, true}, {"posix=0", MOUNT_NOPOSIX, false}, {"posix=1", MOUNT_NOPOSIX, true}, {"text", MOUNT_BINARY, true}, {"user", MOUNT_SYSTEM, true} }; static void usage (FILE *where = stderr) { fprintf (where, "Usage: %s [OPTION] [ ]\n\ %s -a\n\ %s \n\ Display information about mounted filesystems, or mount a filesystem\n\ \n\ -a, --all mount all filesystems mentioned in fstab\n\ -c, --change-cygdrive-prefix change the cygdrive path prefix to \n\ -f, --force force mount, don't warn about missing mount\n\ point directories\n\ -h, --help output usage information and exit\n\ -m, --mount-entries write fstab entries to replicate mount points\n\ and cygdrive prefixes\n\ -o, --options X[,X...] specify mount options\n\ -p, --show-cygdrive-prefix show user and/or system cygdrive path prefix\n\ -v, --version output version information and exit\n\ \n\ Valid options are:\n\n ", progname, progname, progname); for (opt *o = oopts; o < (oopts + (sizeof (oopts) / sizeof (oopts[0]))); o++) fprintf (where, "%s%s", o == oopts ? "" : ",", o->name); fputs ("\n\n", where); exit (where == stderr ? 1 : 0); } static void print_version () { const char *v = strchr (version, ':'); int len; if (!v) { v = "?"; len = 1; } else { v += 2; len = strchr (v, ' ') - v; } printf ("\ %s (cygwin) %.*s\n\ Filesystem Utility\n\ Copyright 1996-2008 Red Hat, Inc.\n\ Compiled on %s\n\ ", progname, len, v, __DATE__); } static char * concat3 (char *a, const char *b, const char *c) { size_t totlen = strlen (a) + strlen (b) + strlen (c) + 1; a = (char *) realloc (a, totlen); return strcat (strcat (a, b), c); } int main (int argc, char **argv) { int i; int flags = MOUNT_BINARY; char *options = strdup (""); enum do_what { nada, saw_change_cygdrive_prefix, saw_show_cygdrive_prefix, saw_mount_commands, saw_mount_all, } do_what = nada; progname = strrchr (argv[0], '/'); if (progname == NULL) progname = strrchr (argv[0], '\\'); if (progname == NULL) progname = argv[0]; else progname++; if (argc == 1) { show_mounts (); exit (0); } while ((i = getopt_long (argc, argv, opts, longopts, NULL)) != EOF) switch (i) { case 'a': if (do_what == nada) do_what = saw_mount_all; else usage (); break; case 'c': if (do_what == nada) do_what = saw_change_cygdrive_prefix; else usage (); break; case 'f': force = true; break; case 'h': usage (stdout); break; case 'm': if (do_what == nada) do_what = saw_mount_commands; else usage (); break; case 'o': if (do_what == saw_mount_all) usage (); else if (*options) options = concat3 (options, ",", optarg); else options = strdup (optarg); break; case 'p': if (do_what == nada) do_what = saw_show_cygdrive_prefix; else usage (); break; case 'v': print_version (); return 0; break; default: usage (); } while (*options) { char *p = strchr (options, ','); if (p) *p++ = '\0'; else p = strchr (options, '\0'); for (opt *o = oopts; o < (oopts + (sizeof (oopts) / sizeof (oopts[0]))); o++) if (strcmp (options, o->name) == 0) { if (o->clear) flags &= ~o->val; else flags |= o->val; goto gotit; } fprintf (stderr, "%s: invalid option - '%s'\n", progname, options); exit (1); gotit: options = p; } if (flags & MOUNT_NOTEXEC && flags & (MOUNT_EXEC | MOUNT_CYGWIN_EXEC)) { fprintf (stderr, "%s: invalid combination of executable options\n", progname); exit (1); } cygwin_internal (CW_SET_DOS_FILE_WARNING, false); argc--; switch (do_what) { case saw_change_cygdrive_prefix: if (optind != argc) usage (); change_cygdrive_prefix (argv[optind], flags); break; case saw_show_cygdrive_prefix: if (optind <= argc) usage (); show_cygdrive_info (); break; case saw_mount_commands: if (optind <= argc) usage (); mount_entries (); break; case saw_mount_all: if (optind <= argc) usage (); do_mount_from_fstab (NULL); break; default: if (optind == argc) do_mount_from_fstab (argv[optind]); else if (optind != (argc - 1)) { fprintf (stderr, "%s: too many arguments\n", progname); usage (); } else if (force || !mount_already_exists (argv[optind + 1], flags)) do_mount (argv[optind], argv[optind + 1], flags); else { errno = EBUSY; error (argv[optind + 1]); } } /* NOTREACHED */ return 0; } static char * convert_spaces (char *tgt, const char *src) { char *tp, *spacep; const char *sp; tp = tgt; for (sp = src; (spacep = strchr (sp, ' ')); sp = spacep + 1) { tp = stpncpy (tp, sp, spacep - sp); tp = stpcpy (tp, "\\040"); } stpcpy (tp, sp); return tgt; } static void mount_entries (void) { FILE *m = setmntent ("/-not-used-", "r"); struct mntent *p; const char *format_mnt = "%s %s %s %s 0 0\n"; const char *format_cyg = "none %s cygdrive %s 0 0\n"; // write fstab entries for normal mount points while ((p = getmntent (m)) != NULL) // Only list non-cygdrives if (!strstr (p->mnt_opts, ",noumount") && !strstr (p->mnt_opts, ",auto")) { char fsname[NT_MAX_PATH], dirname[NT_MAX_PATH]; printf (format_mnt, convert_spaces (fsname, p->mnt_fsname), convert_spaces (dirname, p->mnt_dir), p->mnt_type, p->mnt_opts); } endmntent (m); // write fstab entry for cygdrive prefix m = setmntent ("/-not-used-", "r"); while ((p = getmntent (m)) != NULL) { char *noumount; if ((noumount = strstr (p->mnt_opts, ",noumount"))) { char dirname[NT_MAX_PATH]; char opts[strlen (p->mnt_opts) + 1]; convert_spaces (dirname, p->mnt_dir); // remove trailing slash char *ls = strrchr (dirname, '/'); if (ls) { // last slash == leading slash? cygdrive prefix == "/" if (ls == dirname) ++ls; *ls = '\0'; } *stpncpy (opts, p->mnt_opts, noumount - p->mnt_opts) = '\0'; printf (format_cyg, dirname, opts); break; } } endmntent (m); exit(0); } static void show_mounts (void) { FILE *m = setmntent ("/-not-used-", "r"); struct mntent *p; const char *format = "%s on %s type %s (%s)\n"; // printf (format, "Device", "Directory", "Type", "Flags"); while ((p = getmntent (m)) != NULL) printf (format, p->mnt_fsname, p->mnt_dir, p->mnt_type, p->mnt_opts); endmntent (m); } /* Return 1 if mountpoint from the same registry area is already in mount table. Otherwise return 0. */ static int mount_already_exists (const char *posix_path, int flags) { int found_matching = 0; FILE *m = setmntent ("/-not-used-", "r"); struct mntent *p; while ((p = getmntent (m)) != NULL) { /* if the paths match, and they're both the same type of mount. */ if (strcmp (p->mnt_dir, posix_path) == 0) { if (p->mnt_type[0] == 'u') { if (!(flags & MOUNT_SYSTEM)) /* both current_user */ found_matching = 1; else fprintf (stderr, "%s: warning: system mount point of '%s' " "will always be masked by user mount.\n", progname, posix_path); break; } else if (p->mnt_type[0] == 's') { if (flags & MOUNT_SYSTEM) /* both system */ found_matching = 1; else fprintf (stderr, "%s: warning: user mount point of '%s' " "masks system mount.\n", progname, posix_path); break; } else { fprintf (stderr, "%s: warning: couldn't determine mount type.\n", progname); break; } } } endmntent (m); return found_matching; } /* change_cygdrive_prefix: Change the cygdrive prefix */ static void change_cygdrive_prefix (const char *new_prefix, int flags) { flags |= MOUNT_CYGDRIVE; if (mount (NULL, new_prefix, flags)) error (new_prefix); exit (0); } /* show_cygdrive_info: Show the user and/or cygdrive info, i.e., prefix and flags.*/ static void show_cygdrive_info () { /* Get the cygdrive info */ char user[MAX_PATH]; char system[MAX_PATH]; char user_flags[MAX_PATH]; char system_flags[MAX_PATH]; cygwin_internal (CW_GET_CYGDRIVE_INFO, user, system, user_flags, system_flags); /* Display the user and system cygdrive path prefix, if necessary (ie, not empty) */ const char *format = "%-18s %-11s %s\n"; printf (format, "Prefix", "Type", "Flags"); if (strlen (user) > 0) printf (format, user, "user", user_flags); if (strlen (system) > 0) printf (format, system, "nouser", system_flags); exit (0); }