]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/mount/mount_libmount.c
Merge branch 'sid'
[nfs-utils.git] / utils / mount / mount_libmount.c
1 /*
2  * mount_libmount.c -- Linux NFS [u]mount based on libmount
3  *
4  * Copyright (C) 2011 Karel Zak <kzak@redhat.com>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2, or (at your option)
9  * any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public
17  * License along with this program; if not, write to the
18  * Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA 0211-1301 USA
20  *
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <unistd.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <errno.h>
31 #include <getopt.h>
32
33 #include <libmount/libmount.h>
34
35 #include "nls.h"
36 #include "mount_config.h"
37
38 #include "nfs_mount.h"
39 #include "nfs4_mount.h"
40 #include "stropts.h"
41 #include "version.h"
42 #include "xcommon.h"
43
44 #include "error.h"
45 #include "utils.h"
46
47 char *progname;
48 int nfs_mount_data_version;
49 int verbose;
50 int sloppy;
51 int string;
52 int nomtab;
53
54 #define FOREGROUND      (0)
55 #define BACKGROUND      (1)
56
57 /*
58  * Store mount options to mtab (or /dev/.mount/utab), called from mount.nfs.
59  *
60  * Note that on systems without /etc/mtab the fs-specific options are not
61  * managed by libmount at all. We have to use "mount attributes" that are
62  * private for mount.<type> helpers.
63  */
64 static void store_mount_options(struct libmnt_fs *fs, const char *nfs_opts)
65 {
66         char *o = NULL;
67
68         mnt_fs_set_attributes(fs, nfs_opts);    /* for non-mtab systems */
69
70         /* for mtab create a new options list */
71         mnt_optstr_append_option(&o, mnt_fs_get_vfs_options(fs), NULL);
72         mnt_optstr_append_option(&o, nfs_opts, NULL);
73         mnt_optstr_append_option(&o, mnt_fs_get_user_options(fs), NULL);
74
75         mnt_fs_set_options(fs, o);
76         free(o);
77 }
78
79 /*
80  * Retrieve mount options from mtab (or /dev/.mount/utab) called from umount.nfs.
81  *
82  * The result can passed to free().
83  */
84 char *retrieve_mount_options(struct libmnt_fs *fs)
85 {
86         const char *opts;
87
88         if (!fs)
89                 return NULL;
90
91         opts = mnt_fs_get_attributes(fs);       /* /dev/.mount/utab */
92         if (opts)
93                 return strdup(opts);
94
95         return mnt_fs_strdup_options(fs);       /* /etc/mtab */
96 }
97
98 static int try_mount(struct libmnt_context *cxt, int bg)
99 {
100         struct libmnt_fs *fs;
101         const char *p;
102         char *src = NULL, *tgt = NULL, *type = NULL, *opts = NULL;
103         unsigned long flags = 0;
104         int fake, ret = 0;
105
106         fs = mnt_context_get_fs(cxt);
107
108         /* libmount returns read-only pointers (const char)
109          * so, reallocate for nfsmount() functions.
110          */
111         if ((p = mnt_fs_get_source(fs)))        /* spec */
112                 src = strdup(p);
113         if ((p = mnt_fs_get_target(fs)))        /* mountpoint */
114                 tgt = strdup(p);
115         if ((p = mnt_fs_get_fstype(fs)))        /* FS type */
116                 type = strdup(p);
117         if ((p = mnt_fs_get_fs_options(fs)))    /* mount options */
118                 opts = strdup(p);
119
120         mnt_context_get_mflags(cxt, &flags);    /* mount(2) flags */
121         fake = mnt_context_is_fake(cxt);
122
123         if (string)
124                 ret = nfsmount_string(src, tgt, type, flags, &opts, fake, bg);
125
126         else if (strcmp(type, "nfs4") == 0)
127                 ret = nfs4mount(src, tgt, flags, &opts, fake, bg);
128         else
129                 ret = nfsmount(src, tgt, flags, &opts, fake, bg);
130
131         /* Store mount options if not called with mount --no-mtab */
132         if (!ret && !mnt_context_is_nomtab(cxt))
133                 store_mount_options(fs, opts);
134
135         free(src);
136         free(tgt);
137         free(type);
138         free(opts);
139
140         return ret;
141 }
142
143 /* returns: error = -1, success = 1 , not vers4 == 0 */
144 static int is_vers4(struct libmnt_context *cxt)
145 {
146         struct libmnt_fs *fs = mnt_context_get_fs(cxt);
147         struct libmnt_table *tb = NULL;
148         const char *src = mnt_context_get_source(cxt),
149                    *tgt = mnt_context_get_target(cxt);
150         int rc = 0;
151
152         if (!src || !tgt)
153                 return -1;
154
155         if (!mnt_fs_is_kernel(fs)) {
156                 struct libmnt_table *tb = mnt_new_table_from_file("/proc/mounts");
157
158                 if (!tb)
159                         return -1;
160                 fs = mnt_table_find_pair(tb, src, tgt, MNT_ITER_BACKWARD);
161         }
162
163         if (fs) {
164                 const char *type = mnt_fs_get_fstype(fs);
165                 if (type && strcmp(type, "nfs4") == 0)
166                         rc = 1;
167         }
168         mnt_free_table(tb);
169         return rc;
170 }
171
172 static int umount_main(struct libmnt_context *cxt, int argc, char **argv)
173 {
174         int rc, c;
175         char *spec = NULL, *opts = NULL;
176         int ret = EX_FAIL;
177
178         static const struct option longopts[] = {
179                 { "force", 0, 0, 'f' },
180                 { "help", 0, 0, 'h' },
181                 { "no-mtab", 0, 0, 'n' },
182                 { "verbose", 0, 0, 'v' },
183                 { "read-only", 0, 0, 'r' },
184                 { "lazy", 0, 0, 'l' },
185                 { "types", 1, 0, 't' },
186                 { NULL, 0, 0, 0 }
187         };
188
189         mnt_context_init_helper(cxt, MNT_ACT_UMOUNT, 0);
190
191         while ((c = getopt_long (argc, argv, "fvnrlh", longopts, NULL)) != -1) {
192
193                 rc = mnt_context_helper_setopt(cxt, c, optarg);
194                 if (rc == 0)            /* valid option */
195                         continue;
196                 if (rc < 0)             /* error (probably ENOMEM) */
197                         goto err;
198                                         /* rc==1 means unknow option */
199                 umount_usage();
200                 return EX_USAGE;
201         }
202
203         if (optind < argc)
204                 spec = argv[optind++];
205
206         if (!spec || (*spec != '/' && strchr(spec,':') == NULL)) {
207                 nfs_error(_("%s: no mount point provided"), progname);
208                 return EX_USAGE;
209         }
210
211         if (mnt_context_set_target(cxt, spec))
212                 goto err;
213
214         /* read mtab/fstab, evaluate permissions, etc. */
215         rc = mnt_context_prepare_umount(cxt);
216         if (rc) {
217                 nfs_error(_("%s: failed to prepare umount: %s\n"),
218                                         progname, strerror(-rc));
219                 goto err;
220         }
221
222         if (mnt_context_get_fstype(cxt) &&
223             !mnt_match_fstype(mnt_context_get_fstype(cxt), "nfs,nfs4")) {
224
225                 nfs_error(_("%s: %s: is not an NFS filesystem"), progname, spec);
226                 ret = EX_USAGE;
227                 goto err;
228         }
229
230         opts = retrieve_mount_options(mnt_context_get_fs(cxt));
231
232         if (!mnt_context_is_lazy(cxt)) {
233                 if (opts) {
234                         /* we have full FS description (e.g. from mtab or /proc) */
235                         switch (is_vers4(cxt)) {
236                         case 0:
237                                 /* We ignore the error from nfs_umount23.
238                                  * If the actual umount succeeds (in del_mtab),
239                                  * we don't want to signal an error, as that
240                                  * could cause /sbin/mount to retry!
241                                  */
242                                 nfs_umount23(mnt_context_get_source(cxt), opts);
243                                 break;
244                         case 1:                 /* unknown */
245                                 break;
246                         default:                /* error */
247                                 goto err;
248                         }
249                 } else
250                         /* strange, no entry in mtab or /proc not mounted */
251                         nfs_umount23(spec, "tcp,v3");
252         }
253
254         ret = EX_FILEIO;
255         rc = mnt_context_do_umount(cxt);        /* call umount(2) syscall */
256         mnt_context_finalize_mount(cxt);        /* mtab update */
257
258         if (rc && !mnt_context_get_status(cxt)) {
259                 /* mnt_context_do_umount() returns errno if umount(2) failed */
260                 umount_error(rc, spec);
261                 goto err;
262         }
263         ret = EX_SUCCESS;
264 err:
265         free(opts);
266         return ret;
267 }
268
269 static int mount_main(struct libmnt_context *cxt, int argc, char **argv)
270 {
271         int rc, c;
272         struct libmnt_fs *fs;
273         char *spec = NULL, *mount_point = NULL, *opts = NULL;
274
275         static const struct option longopts[] = {
276           { "fake", 0, 0, 'f' },
277           { "help", 0, 0, 'h' },
278           { "no-mtab", 0, 0, 'n' },
279           { "read-only", 0, 0, 'r' },
280           { "ro", 0, 0, 'r' },
281           { "verbose", 0, 0, 'v' },
282           { "version", 0, 0, 'V' },
283           { "read-write", 0, 0, 'w' },
284           { "rw", 0, 0, 'w' },
285           { "options", 1, 0, 'o' },
286           { "sloppy", 0, 0, 's' },
287           { NULL, 0, 0, 0 }
288         };
289
290         mount_config_init(progname);
291         mnt_context_init_helper(cxt, MNT_ACT_MOUNT, 0);
292
293         while ((c = getopt_long(argc, argv, "fhnrVvwo:s", longopts, NULL)) != -1) {
294
295                 rc = mnt_context_helper_setopt(cxt, c, optarg);
296                 if (rc == 0)            /* valid option */
297                         continue;
298                 if (rc < 0)             /* error (probably ENOMEM) */
299                         goto err;
300                                         /* rc==1 means unknow option */
301                 switch (c) {
302                 case 'V':
303                         printf("%s: ("PACKAGE_STRING")\n", progname);
304                         return EX_SUCCESS;
305                 case 'h':
306                 default:
307                         mount_usage();
308                         return EX_USAGE;
309                 }
310         }
311
312         if (optind < argc)
313                 spec = argv[optind++];
314         if (optind < argc)
315                 mount_point = argv[optind++];
316
317         if (!mount_point) {
318                 nfs_error(_("%s: no mount point provided"), progname);
319                 goto err;
320         }
321         if (!spec) {
322                 nfs_error(_("%s: no mount spec provided"), progname);
323                 goto err;
324         }
325
326         if (geteuid() != 0) {
327                 nfs_error(_("%s: not installed setuid - "
328                             "\"user\" NFS mounts not supported."), progname);
329                 goto err;
330         }
331
332         verbose = mnt_context_is_verbose(cxt);
333         sloppy = mnt_context_is_sloppy(cxt);
334         nomtab = mnt_context_is_nomtab(cxt);
335
336         if (strcmp(progname, "mount.nfs4") == 0)
337                 mnt_context_set_fstype(cxt, "nfs4");
338         else
339                 mnt_context_set_fstype(cxt, "nfs");     /* default */
340
341         rc = mnt_context_set_source(cxt, spec);
342         if (!rc)
343                 mnt_context_set_target(cxt, mount_point);
344         if (rc) {
345                 nfs_error(_("%s: failed to set spec or mountpoint: %s"),
346                                 progname, strerror(errno));
347                 goto err;
348         }
349
350         mount_point = mnt_resolve_path(mount_point,
351                                        mnt_context_get_cache(cxt));
352
353         if (chk_mountpoint(mount_point))
354                 goto err;
355
356         /*
357          * The libmount strictly uses only options from fstab if running in
358          * restricted mode (suid, non-root user). This is done in
359          * mnt_context_prepare_mount() by default.
360          *
361          * We have to read fstab before nfsmount.conf, otherwise the options
362          * from nfsmount.conf will be ignored (overwrited).
363          */
364         rc = mnt_context_apply_fstab(cxt);
365         if (rc) {
366                 nfs_error(_("%s: failed to apply fstab options\n"), progname);
367                 goto err;
368         }
369
370         /*
371          * Concatenate mount options from the configuration file
372          */
373         fs = mnt_context_get_fs(cxt);
374         if (fs) {
375                 opts = mnt_fs_strdup_options(fs);
376
377                 opts = mount_config_opts(spec, mount_point, opts);
378                 mnt_fs_set_options(fs, opts);
379         }
380
381         rc = mnt_context_prepare_mount(cxt);
382         if (rc) {
383                 nfs_error(_("%s: failed to prepare mount: %s\n"),
384                                         progname, strerror(-rc));
385                 goto err;
386         }
387
388         rc = try_mount(cxt, FOREGROUND);
389
390         if (rc == EX_BG) {
391                 printf(_("%s: backgrounding \"%s\"\n"),
392                         progname, mnt_context_get_source(cxt));
393                 printf(_("%s: mount options: \"%s\"\n"),
394                         progname, opts);
395
396                 fflush(stdout);
397
398                 if (daemon(0, 0)) {
399                         nfs_error(_("%s: failed to start "
400                                         "background process: %s\n"),
401                                         progname, strerror(errno));
402                         exit(EX_FAIL);
403                 }
404
405                 rc = try_mount(cxt, BACKGROUND);
406
407                 if (verbose && rc)
408                         printf(_("%s: giving up \"%s\"\n"),
409                                 progname, mnt_context_get_source(cxt));
410         }
411
412         mnt_context_set_syscall_status(cxt, rc == EX_SUCCESS ? 0 : -1);
413         mnt_context_finalize_mount(cxt);        /* mtab update */
414         return rc;
415 err:
416         return EX_FAIL;
417 }
418
419 int main(int argc, char *argv[])
420 {
421         struct libmnt_context *cxt;
422         int rc;
423
424         mnt_init_debug(0);
425         cxt = mnt_new_context();
426         if (!cxt) {
427                 nfs_error(_("Can't initilize libmount: %s"),
428                                         strerror(errno));
429                 rc = EX_FAIL;
430                 goto done;
431         }
432
433         progname = basename(argv[0]);
434         nfs_mount_data_version = discover_nfs_mount_data_version(&string);
435
436         if(strncmp(progname, "umount", 6) == 0)
437                 rc = umount_main(cxt, argc, argv);
438         else
439                 rc = mount_main(cxt, argc, argv);
440 done:
441         mnt_free_context(cxt);
442         return rc;
443 }