]> sourceware.org Git - newlib-cygwin.git/blame - winsup/utils/mount.cc
* Merge in cygwin-64bit-branch.
[newlib-cygwin.git] / winsup / utils / mount.cc
CommitLineData
1fd5e000
CF
1/* mount.cc
2
df0f949c 3 Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
61522196 4 2007, 2008, 2009, 2010, 2011, 2013 Red Hat, Inc.
1fd5e000
CF
5
6This file is part of Cygwin.
7
8This software is a copyrighted work licensed under the terms of the
9Cygwin license. Please consult the file "CYGWIN_LICENSE" for
10details. */
11
12#include <stdio.h>
13#include <sys/mount.h>
14#include <sys/stat.h>
15#include <mntent.h>
16#include <windows.h>
17#include <sys/cygwin.h>
92b499ac 18#include <cygwin/version.h>
b6e259b1 19#include <stdlib.h>
1e497ebd 20#include <unistd.h>
be61cf4d 21#include <getopt.h>
035df9ef 22#include <dirent.h>
1e497ebd 23#include "path.h"
1fd5e000 24
1fd5e000
CF
25#include <errno.h>
26
56d81795
CV
27#define NT_MAX_PATH 32768
28
48897dfe
CF
29#define EXEC_FLAGS (MOUNT_EXEC | MOUNT_NOTEXEC | MOUNT_CYGWIN_EXEC)
30
56d81795 31static void mount_entries (void);
1fd5e000 32static void show_mounts (void);
4c35f9f0 33static void show_cygdrive_info (void);
1fd5e000
CF
34static void change_cygdrive_prefix (const char *new_prefix, int flags);
35static int mount_already_exists (const char *posix_path, int flags);
36
37// static short create_missing_dirs = FALSE;
1e497ebd 38static bool force = false;
1fd5e000
CF
39
40static const char *progname;
41
9500a3db
CV
42static void
43error (const char *path)
44{
45 fprintf (stderr, "%s: %s: %s\n", progname, path,
46 (errno == EMFILE) ? "Too many mount entries" : strerror (errno));
47 exit (1);
48}
49
1fd5e000
CF
50/* FIXME: do_mount should also print a warning message if the dev arg
51 is a non-existent Win32 path. */
52
53static void
54do_mount (const char *dev, const char *where, int flags)
55{
56 struct stat statbuf;
1fd5e000
CF
57 int statres;
58
c2b88623 59 statres = stat (where, &statbuf);
1fd5e000
CF
60
61#if 0
62 if (statres == -1)
63 {
64 /* FIXME: this'll fail if mount dir is missing any parent dirs */
65 if (create_missing_dirs == TRUE)
66 {
67 if (mkdir (where, 0755) == -1)
68 fprintf (stderr, "Warning: unable to create %s!\n", where);
69 else
70 statres = 0; /* Pretend stat succeeded if we could mkdir. */
71 }
72 }
73#endif
74
1fd5e000
CF
75 if (statres == -1)
76 {
48897dfe 77 if (!force)
1fd5e000
CF
78 fprintf (stderr, "%s: warning - %s does not exist.\n", progname, where);
79 }
80 else if (!(statbuf.st_mode & S_IFDIR))
81 {
48897dfe 82 if (!force)
92b499ac
CV
83 fprintf (stderr, "%s: warning: %s is not a directory.\n",
84 progname, where);
eedc36cb 85 }
1fd5e000 86
68a178b3 87 if (!force && !(flags & (EXEC_FLAGS | MOUNT_BIND)) && strlen (dev))
48897dfe
CF
88 {
89 char devtmp[1 + 2 * strlen (dev)];
90 strcpy (devtmp, dev);
91 char c = strchr (devtmp, '\0')[-1];
92 if (c == '/' || c == '\\')
93 strcat (devtmp, ".");
94 /* Use a curious property of Windows which allows the use of \.. even
4bfc614b 95 on non-directory paths. */
48897dfe
CF
96 for (const char *p = dev; (p = strpbrk (p, "/\\")); p++)
97 strcat (devtmp, "\\..");
98 strcat (devtmp, "\\");
99 if (GetDriveType (devtmp) == DRIVE_REMOTE)
100 {
92b499ac
CV
101 fprintf (stderr,
102 "%s: defaulting to 'notexec' mount option for speed since native path\n"
103 "%*creferences a remote share. Use '-f' option to override.\n",
61522196 104 progname, (int) strlen(progname) + 2, ' ');
48897dfe
CF
105 flags |= MOUNT_NOTEXEC;
106 }
107 }
108
109 if (mount (dev, where, flags))
110 error (where);
1fd5e000
CF
111}
112
1e497ebd
CV
113static void
114from_fstab (bool user)
115{
116 char path[PATH_MAX];
117 char buf[65536];
118 mnt_t *m = mount_table + max_mount_entry;
119
120 strcpy (path, "/etc/fstab");
121 if (user)
122 {
123 strcat (path, ".d/");
124 strcat (path, getlogin ());
125 }
126 FILE *fh = fopen (path, "rt");
127 if (!fh)
128 return;
129 while (fgets (buf, 65536, fh))
130 {
131 char *c = strrchr (buf, '\n');
5578c337 132 if (c)
1e497ebd
CV
133 *c = '\0';
134 if (from_fstab_line (m, buf, user))
135 ++m;
136 }
137 max_mount_entry = m - mount_table;
138 fclose (fh);
139}
140
141static void
142do_mount_from_fstab (const char *where)
143{
144 force = true;
145 /* Read fstab entries. */
146 from_fstab (false);
147 from_fstab (true);
148 /* Loop through fstab entries and see if it matches `where'. If `where'
149 is NULL, all entries match. */
150 bool exists = false;
151 for (mnt_t *m = mount_table; m - mount_table < max_mount_entry; ++m)
f380ca58 152 if (!where || !strcmp (where, m->posix))
1e497ebd 153 {
f380ca58
CV
154 if (m->flags & MOUNT_CYGDRIVE)
155 {
156 /* Get the cygdrive info */
157 char user[MAX_PATH];
158 char system[MAX_PATH];
159 char user_flags[MAX_PATH];
160 char system_flags[MAX_PATH];
161
162 exists = true;
163 cygwin_internal (CW_GET_CYGDRIVE_INFO, user, system, user_flags,
164 system_flags);
165 if ((*user && strcmp (user, m->posix) != 0)
166 || (*system && strcmp (system, m->posix) != 0))
167 if (mount (NULL, m->posix, m->flags))
168 error (m->posix);
169 }
170 else
171 {
172 exists = true;
173 /* Compare with existing mount table. If the entry doesn't exist,
174 mount it. */
175 FILE *mt = setmntent ("/-not-used-", "r");
176 struct mntent *p;
177
178 while ((p = getmntent (mt)) != NULL)
179 if (!strcmp (m->posix, p->mnt_dir))
180 break;
181 if (!p)
182 do_mount (m->native, m->posix, m->flags);
183 endmntent (mt);
184 if (where)
185 break;
186 }
1e497ebd
CV
187 }
188 if (!exists && where)
189 fprintf (stderr,
190 "%s: can't find %s in /etc/fstab or in /etc/fstab.d/$USER\n",
191 progname, where);
192}
193
c49fa762 194static struct option longopts[] =
be61cf4d 195{
1e497ebd 196 {"all", no_argument, NULL, 'a'},
be61cf4d 197 {"change-cygdrive-prefix", no_argument, NULL, 'c'},
8704ad9d
CF
198 {"force", no_argument, NULL, 'f'},
199 {"help", no_argument, NULL, 'h' },
56d81795 200 {"mount-entries", no_argument, NULL, 'm'},
bb002a49 201 {"options", required_argument, NULL, 'o'},
8704ad9d 202 {"show-cygdrive-prefix", no_argument, NULL, 'p'},
92b499ac 203 {"version", no_argument, NULL, 'V'},
be61cf4d
CF
204 {NULL, 0, NULL, 0}
205};
206
92b499ac 207static char opts[] = "acfhmpVo:";
56d81795 208
1fd5e000 209static void
8704ad9d 210usage (FILE *where = stderr)
1fd5e000 211{
a82a8dcb
CV
212 char *options;
213
92b499ac
CV
214 fprintf (where, "Usage: %1$s [OPTION] [<win32path> <posixpath>]\n\
215 %1$s -a\n\
216 %1$s <posixpath>\n\
217\n\
aa275fe0
JDF
218Display information about mounted filesystems, or mount a filesystem\n\
219\n\
1e497ebd 220 -a, --all mount all filesystems mentioned in fstab\n\
be61cf4d
CF
221 -c, --change-cygdrive-prefix change the cygdrive path prefix to <posixpath>\n\
222 -f, --force force mount, don't warn about missing mount\n\
9f425256 223 point directories\n\
8704ad9d 224 -h, --help output usage information and exit\n\
56d81795
CV
225 -m, --mount-entries write fstab entries to replicate mount points\n\
226 and cygdrive prefixes\n\
08604f02 227 -o, --options X[,X...] specify mount options\n\
be61cf4d 228 -p, --show-cygdrive-prefix show user and/or system cygdrive path prefix\n\
92b499ac
CV
229 -V, --version output version information and exit\n\n",
230 progname);
a82a8dcb
CV
231 if (!cygwin_internal (CW_LST_MNT_OPTS, &options))
232 fprintf (where, "Valid options are: %s\n\n", options);
8704ad9d
CF
233 exit (where == stderr ? 1 : 0);
234}
235
236static void
237print_version ()
238{
92b499ac 239 printf ("mount (cygwin) %d.%d.%d\n"
1b23b30b
CF
240 "Mount filesystem utility\n"
241 "Copyright (C) 1996 - %s Red Hat, Inc.\n"
242 "This is free software; see the source for copying conditions. There is NO\n"
92b499ac 243 "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n",
1b23b30b
CF
244 CYGWIN_VERSION_DLL_MAJOR / 1000,
245 CYGWIN_VERSION_DLL_MAJOR % 1000,
246 CYGWIN_VERSION_DLL_MINOR,
247 strrchr (__DATE__, ' ') + 1);
1fd5e000
CF
248}
249
08604f02
CF
250static char *
251concat3 (char *a, const char *b, const char *c)
252{
253 size_t totlen = strlen (a) + strlen (b) + strlen (c) + 1;
254 a = (char *) realloc (a, totlen);
255 return strcat (strcat (a, b), c);
256}
257
1fd5e000 258int
be61cf4d 259main (int argc, char **argv)
1fd5e000
CF
260{
261 int i;
b050246c 262 int flags = MOUNT_BINARY;
08604f02 263 char *options = strdup ("");
be61cf4d
CF
264 enum do_what
265 {
266 nada,
267 saw_change_cygdrive_prefix,
bee722a5 268 saw_show_cygdrive_prefix,
1e497ebd
CV
269 saw_mount_commands,
270 saw_mount_all,
be61cf4d 271 } do_what = nada;
1fd5e000 272
92b499ac 273 progname = program_invocation_short_name;
1fd5e000
CF
274
275 if (argc == 1)
276 {
277 show_mounts ();
278 exit (0);
279 }
280
be61cf4d
CF
281 while ((i = getopt_long (argc, argv, opts, longopts, NULL)) != EOF)
282 switch (i)
283 {
1e497ebd
CV
284 case 'a':
285 if (do_what == nada)
286 do_what = saw_mount_all;
287 else
288 usage ();
289 break;
9f425256 290 case 'c':
be61cf4d
CF
291 if (do_what == nada)
292 do_what = saw_change_cygdrive_prefix;
293 else
294 usage ();
295 break;
296 case 'f':
1e497ebd 297 force = true;
be61cf4d 298 break;
8704ad9d
CF
299 case 'h':
300 usage (stdout);
301 break;
8704ad9d
CF
302 case 'm':
303 if (do_what == nada)
304 do_what = saw_mount_commands;
305 else
306 usage ();
307 break;
08604f02 308 case 'o':
1e497ebd
CV
309 if (do_what == saw_mount_all)
310 usage ();
311 else if (*options)
08604f02
CF
312 options = concat3 (options, ",", optarg);
313 else
314 options = strdup (optarg);
315 break;
be61cf4d
CF
316 case 'p':
317 if (do_what == nada)
318 do_what = saw_show_cygdrive_prefix;
319 else
320 usage ();
321 break;
92b499ac 322 case 'V':
8704ad9d
CF
323 print_version ();
324 return 0;
be61cf4d 325 break;
be61cf4d 326 default:
92b499ac
CV
327 fprintf (stderr, "Try `%s --help' for more information.\n", progname);
328 return 1;
be61cf4d 329 }
eedc36cb 330
a82a8dcb 331 if (cygwin_internal (CW_CVT_MNT_OPTS, &options, &flags))
08604f02 332 {
08604f02
CF
333 fprintf (stderr, "%s: invalid option - '%s'\n", progname, options);
334 exit (1);
08604f02
CF
335 }
336
48897dfe
CF
337 if (flags & MOUNT_NOTEXEC && flags & (MOUNT_EXEC | MOUNT_CYGWIN_EXEC))
338 {
92b499ac
CV
339 fprintf (stderr, "%s: invalid combination of executable options\n",
340 progname);
48897dfe
CF
341 exit (1);
342 }
343
e7fd6e57
CF
344 cygwin_internal (CW_SET_DOS_FILE_WARNING, false);
345
be61cf4d
CF
346 argc--;
347 switch (do_what)
1fd5e000 348 {
be61cf4d
CF
349 case saw_change_cygdrive_prefix:
350 if (optind != argc)
351 usage ();
f7483b41 352 change_cygdrive_prefix (argv[optind], flags);
be61cf4d 353 break;
be61cf4d
CF
354 case saw_show_cygdrive_prefix:
355 if (optind <= argc)
356 usage ();
357 show_cygdrive_info ();
358 break;
bee722a5
CF
359 case saw_mount_commands:
360 if (optind <= argc)
361 usage ();
56d81795 362 mount_entries ();
bee722a5 363 break;
1e497ebd
CV
364 case saw_mount_all:
365 if (optind <= argc)
1b23b30b 366 usage ();
1e497ebd
CV
367 do_mount_from_fstab (NULL);
368 break;
be61cf4d 369 default:
1e497ebd
CV
370 if (optind == argc)
371 do_mount_from_fstab (argv[optind]);
372 else if (optind != (argc - 1))
be61cf4d 373 {
1e497ebd 374 fprintf (stderr, "%s: too many arguments\n", progname);
be61cf4d
CF
375 usage ();
376 }
1e497ebd 377 else if (force || !mount_already_exists (argv[optind + 1], flags))
f7483b41 378 do_mount (argv[optind], argv[optind + 1], flags);
be61cf4d
CF
379 else
380 {
381 errno = EBUSY;
382 error (argv[optind + 1]);
383 }
1fd5e000 384 }
1fd5e000
CF
385
386 /* NOTREACHED */
387 return 0;
388}
389
56d81795
CV
390static char *
391convert_spaces (char *tgt, const char *src)
392{
393 char *tp, *spacep;
394 const char *sp;
395
396 tp = tgt;
397 for (sp = src; (spacep = strchr (sp, ' ')); sp = spacep + 1)
398 {
399 tp = stpncpy (tp, sp, spacep - sp);
400 tp = stpcpy (tp, "\\040");
401 }
402 stpcpy (tp, sp);
403 return tgt;
404}
405
bee722a5 406static void
56d81795 407mount_entries (void)
bee722a5
CF
408{
409 FILE *m = setmntent ("/-not-used-", "r");
410 struct mntent *p;
56d81795
CV
411 const char *format_mnt = "%s %s %s %s 0 0\n";
412 const char *format_cyg = "none %s cygdrive %s 0 0\n";
bee722a5 413
56d81795 414 // write fstab entries for normal mount points
e48feef9 415 while ((p = getmntent (m)) != NULL)
68a178b3 416 // Only list non-cygdrives and non-automounts
e9517437 417 if (!strstr (p->mnt_opts, ",noumount") && !strstr (p->mnt_opts, ",auto"))
e48feef9 418 {
56d81795 419 char fsname[NT_MAX_PATH], dirname[NT_MAX_PATH];
68a178b3
CV
420 /* Drop the "bind" option since it can't be reverted. */
421 char *c = strstr (p->mnt_opts, ",bind");
422 if (c)
423 memmove (c, c + 5, strlen (c + 5) + 1);
56d81795
CV
424 printf (format_mnt, convert_spaces (fsname, p->mnt_fsname),
425 convert_spaces (dirname, p->mnt_dir),
426 p->mnt_type, p->mnt_opts);
e48feef9 427 }
bee722a5
CF
428 endmntent (m);
429
56d81795
CV
430 // write fstab entry for cygdrive prefix
431 m = setmntent ("/-not-used-", "r");
432 while ((p = getmntent (m)) != NULL)
e48feef9 433 {
56d81795
CV
434 char *noumount;
435 if ((noumount = strstr (p->mnt_opts, ",noumount")))
1b23b30b 436 {
56d81795
CV
437 char dirname[NT_MAX_PATH];
438 char opts[strlen (p->mnt_opts) + 1];
439
440 convert_spaces (dirname, p->mnt_dir);
8262e642 441 // remove trailing slash
56d81795 442 char *ls = strrchr (dirname, '/');
8262e642
CV
443 if (ls)
444 {
445 // last slash == leading slash? cygdrive prefix == "/"
446 if (ls == dirname)
447 ++ls;
448 *ls = '\0';
449 }
56d81795
CV
450 *stpncpy (opts, p->mnt_opts, noumount - p->mnt_opts) = '\0';
451 printf (format_cyg, dirname, opts);
452 break;
453 }
e48feef9 454 }
56d81795 455 endmntent (m);
1b23b30b 456
bee722a5
CF
457 exit(0);
458}
459
1fd5e000
CF
460static void
461show_mounts (void)
462{
463 FILE *m = setmntent ("/-not-used-", "r");
464 struct mntent *p;
c6cd25a0 465 const char *format = "%s on %s type %s (%s)\n";
1fd5e000 466
c6cd25a0 467 // printf (format, "Device", "Directory", "Type", "Flags");
1fd5e000 468 while ((p = getmntent (m)) != NULL)
c6cd25a0 469 printf (format, p->mnt_fsname, p->mnt_dir, p->mnt_type, p->mnt_opts);
1fd5e000
CF
470 endmntent (m);
471}
472
473/* Return 1 if mountpoint from the same registry area is already in
474 mount table. Otherwise return 0. */
475static int
476mount_already_exists (const char *posix_path, int flags)
477{
478 int found_matching = 0;
479
480 FILE *m = setmntent ("/-not-used-", "r");
481 struct mntent *p;
482
483 while ((p = getmntent (m)) != NULL)
484 {
485 /* if the paths match, and they're both the same type of mount. */
486 if (strcmp (p->mnt_dir, posix_path) == 0)
487 {
44d2afed 488 if (p->mnt_type[0] == 'u')
1fd5e000 489 {
9f425256
CF
490 if (!(flags & MOUNT_SYSTEM)) /* both current_user */
491 found_matching = 1;
492 else
493 fprintf (stderr,
494 "%s: warning: system mount point of '%s' "
495 "will always be masked by user mount.\n",
496 progname, posix_path);
1fd5e000 497 break;
9f425256 498 }
44d2afed 499 else if (p->mnt_type[0] == 's')
1fd5e000 500 {
9f425256
CF
501 if (flags & MOUNT_SYSTEM) /* both system */
502 found_matching = 1;
503 else
504 fprintf (stderr,
505 "%s: warning: user mount point of '%s' "
92b499ac 506 "masks system mount.\n", progname, posix_path);
1fd5e000
CF
507 break;
508 }
509 else
510 {
92b499ac
CV
511 fprintf (stderr, "%s: warning: couldn't determine mount type.\n",
512 progname);
1fd5e000
CF
513 break;
514 }
515 }
516 }
517 endmntent (m);
518
519 return found_matching;
520}
521
522/* change_cygdrive_prefix: Change the cygdrive prefix */
523static void
524change_cygdrive_prefix (const char *new_prefix, int flags)
525{
94cc482c 526 flags |= MOUNT_CYGDRIVE;
1fd5e000
CF
527
528 if (mount (NULL, new_prefix, flags))
9500a3db 529 error (new_prefix);
eedc36cb 530
1fd5e000
CF
531 exit (0);
532}
959e1bac 533
be61cf4d 534/* show_cygdrive_info: Show the user and/or cygdrive info, i.e., prefix and
4c35f9f0 535 flags.*/
959e1bac 536static void
4c35f9f0 537show_cygdrive_info ()
959e1bac 538{
4c35f9f0 539 /* Get the cygdrive info */
959e1bac
CF
540 char user[MAX_PATH];
541 char system[MAX_PATH];
4c35f9f0
CF
542 char user_flags[MAX_PATH];
543 char system_flags[MAX_PATH];
544 cygwin_internal (CW_GET_CYGDRIVE_INFO, user, system, user_flags,
545 system_flags);
959e1bac 546
be61cf4d 547 /* Display the user and system cygdrive path prefix, if necessary
959e1bac 548 (ie, not empty) */
4c35f9f0
CF
549 const char *format = "%-18s %-11s %s\n";
550 printf (format, "Prefix", "Type", "Flags");
959e1bac 551 if (strlen (user) > 0)
4c35f9f0 552 printf (format, user, "user", user_flags);
959e1bac 553 if (strlen (system) > 0)
56d81795 554 printf (format, system, "nouser", system_flags);
959e1bac
CF
555
556 exit (0);
557}
This page took 0.314877 seconds and 5 git commands to generate.