xref: /illumos-gate/usr/src/cmd/fs.d/smbclnt/mount/mount.c (revision e557d412)
1 /*
2  * Copyright (c) 2000-2001, Boris Popov
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *    This product includes software developed by Boris Popov.
16  * 4. Neither the name of the author nor the names of any co-contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  * $Id: mount_smbfs.c,v 1.28.44.2 2005/06/02 00:55:41 lindak Exp $
33  */
34 
35 /*
36  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
37  * Use is subject to license terms.
38  */
39 
40 #include <stdio.h>
41 #include <string.h>
42 #include <strings.h>
43 #include <pwd.h>
44 #include <grp.h>
45 #include <unistd.h>
46 #include <ctype.h>
47 #include <stdlib.h>
48 #include <errno.h>
49 #include <err.h>
50 #include <libintl.h>
51 #include <locale.h>
52 #include <libscf.h>
53 
54 #include <sys/types.h>
55 #include <sys/stat.h>
56 #include <sys/errno.h>
57 #include <sys/mount.h>
58 #include <sys/mntent.h>
59 #include <sys/mnttab.h>
60 
61 #include <sys/fs/smbfs_mount.h>
62 
63 /* This needs to know ctx->ct_dev_fd, etc. */
64 #include <netsmb/smb_lib.h>
65 
66 extern char *optarg;
67 extern int optind;
68 int enable_noacl_option = 0;
69 
70 static char mount_point[MAXPATHLEN + 1];
71 static void usage(void);
72 static int setsubopt(smb_ctx_t *, struct smbfs_args *, char *);
73 
74 const char * const optlist[] = {
75 
76 	/* Generic VFS options. */
77 #define	OPT_RO		0
78 	MNTOPT_RO,
79 #define	OPT_RW		1
80 	MNTOPT_RW,
81 #define	OPT_SUID 	2
82 	MNTOPT_SUID,
83 #define	OPT_NOSUID 	3
84 	MNTOPT_NOSUID,
85 #define	OPT_DEVICES	4
86 	MNTOPT_DEVICES,
87 #define	OPT_NODEVICES	5
88 	MNTOPT_NODEVICES,
89 #define	OPT_SETUID	6
90 	MNTOPT_SETUID,
91 #define	OPT_NOSETUID	7
92 	MNTOPT_NOSETUID,
93 #define	OPT_EXEC	8
94 	MNTOPT_EXEC,
95 #define	OPT_NOEXEC	9
96 	MNTOPT_NOEXEC,
97 #define	OPT_XATTR	10
98 	MNTOPT_XATTR,
99 #define	OPT_NOXATTR	11
100 	MNTOPT_NOXATTR,
101 
102 	/* Sort of generic (from NFS) */
103 #define	OPT_NOAC	12
104 	MNTOPT_NOAC,
105 #define	OPT_ACTIMEO	13
106 	MNTOPT_ACTIMEO,
107 #define	OPT_ACREGMIN	14
108 	MNTOPT_ACREGMIN,
109 #define	OPT_ACREGMAX	15
110 	MNTOPT_ACREGMAX,
111 #define	OPT_ACDIRMIN	16
112 	MNTOPT_ACDIRMIN,
113 #define	OPT_ACDIRMAX	17
114 	MNTOPT_ACDIRMAX,
115 
116 	/* smbfs-specifis options */
117 #define	OPT_DOMAIN	18
118 	"domain",
119 #define	OPT_USER	19
120 	"user",
121 #define	OPT_UID		20
122 	"uid",
123 #define	OPT_GID		21
124 	"gid",
125 #define	OPT_DIRPERMS	22
126 	"dirperms",
127 #define	OPT_FILEPERMS	23
128 	"fileperms",
129 #define	OPT_NOPROMPT	24
130 	"noprompt",
131 #define	OPT_ACL		25
132 	MNTOPT_ACL,
133 #define	OPT_NOACL	26
134 	MNTOPT_NOACL,
135 
136 	NULL
137 };
138 
139 static int Oflg = 0;    /* Overlay mounts */
140 static int qflg = 0;    /* quiet - don't print warnings on bad options */
141 static int noprompt = 0;	/* don't prompt for password */
142 
143 /* Note: smbfs uses _both_ kinds of options. */
144 static int mntflags = MS_DATA | MS_OPTIONSTR;
145 
146 #define	EX_OK	0	/* normal */
147 #define	EX_OPT	1	/* bad options, usage, etc */
148 #define	EX_MNT	2	/* mount point problems, etc */
149 #define	RET_ERR	3	/* later errors */
150 
151 #define	SERVICE "svc:/network/smb/client:default"
152 
153 struct smbfs_args mdata;
154 struct mnttab mnt;
155 
156 /*
157  * Initialize this with "rw" just to have something there,
158  * so we don't have to decide whether to add a comma when
159  * we strcat another option.  Note the "rw" may be changed
160  * to an "ro" by option processing.
161  */
162 char optbuf[MAX_MNTOPT_STR] = "rw";
163 
164 int
165 main(int argc, char *argv[])
166 {
167 	struct smb_ctx *ctx = NULL;
168 	struct stat st;
169 	int opt, error, err2;
170 	static char *fstype = MNTTYPE_SMBFS;
171 	char *env, *state;
172 
173 	(void) setlocale(LC_ALL, "");
174 #if !defined(TEXT_DOMAIN)
175 #define	TEXT_DOMAIN	"SYS_TEST"
176 #endif
177 	(void) textdomain(TEXT_DOMAIN);
178 	if (argc == 2) {
179 		if (strcmp(argv[1], "-h") == 0) {
180 			usage();
181 		} else if (strcmp(argv[1], "-v") == 0) {
182 			errx(EX_OK, gettext("version %d.%d.%d"),
183 			    SMBFS_VERSION / 100000,
184 			    (SMBFS_VERSION % 10000) / 1000,
185 			    (SMBFS_VERSION % 1000) / 100);
186 		}
187 	}
188 	if (argc < 3)
189 		usage();
190 
191 	state = smf_get_state(SERVICE);
192 	if (state == NULL || strcmp(state, SCF_STATE_STRING_ONLINE) != 0) {
193 		fprintf(stderr,
194 		    gettext("mount_smbfs: service \"%s\" not enabled.\n"),
195 		    SERVICE);
196 		exit(RET_ERR);
197 	}
198 	free(state);
199 
200 	/* Debugging support. */
201 	if ((env = getenv("SMBFS_DEBUG")) != NULL) {
202 		smb_debug = atoi(env);
203 		if (smb_debug < 1)
204 			smb_debug = 1;
205 	}
206 
207 	error = smb_lib_init();
208 	if (error)
209 		exit(RET_ERR);
210 
211 	mnt.mnt_mntopts = optbuf;
212 
213 	bzero(&mdata, sizeof (mdata));
214 	mdata.version = SMBFS_VERSION;		/* smbfs mount version */
215 	mdata.uid = (uid_t)-1;
216 	mdata.gid = (gid_t)-1;
217 
218 	error = smb_ctx_alloc(&ctx);
219 	if (error)
220 		exit(RET_ERR);
221 
222 	/*
223 	 * Parse the UNC path so we have the server (etc.)
224 	 * that we need during rcfile+sharectl parsing.
225 	 */
226 	if (argc < 3)
227 		usage();
228 	error = smb_ctx_parseunc(ctx, argv[argc - 2],
229 	    SMBL_SHARE, SMBL_SHARE, USE_DISKDEV, NULL);
230 	if (error)
231 		exit(EX_OPT);
232 
233 	error = smb_ctx_readrc(ctx);
234 	if (error)
235 		exit(EX_OPT);
236 
237 	while ((opt = getopt(argc, argv, "ro:Oq")) != -1) {
238 		switch (opt) {
239 		case 'O':
240 			Oflg++;
241 			break;
242 
243 		case 'q':
244 			qflg++;
245 			break;
246 
247 		case 'r':
248 			mntflags |= MS_RDONLY;
249 			break;
250 
251 		case 'o': {
252 			char *nextopt, *comma, *sopt;
253 			int ret;
254 
255 			for (sopt = optarg; sopt != NULL; sopt = nextopt) {
256 				comma = strchr(sopt, ',');
257 				if (comma) {
258 					nextopt = comma + 1;
259 					*comma = '\0';
260 				} else
261 					nextopt = NULL;
262 				ret = setsubopt(ctx, &mdata, sopt);
263 				if (ret != 0)
264 					exit(EX_OPT);
265 				/* undo changes to optarg */
266 				if (comma)
267 					*comma = ',';
268 			}
269 			break;
270 		}
271 
272 		case '?':
273 		default:
274 			usage();
275 		}
276 	}
277 
278 	if (Oflg)
279 		mntflags |= MS_OVERLAY;
280 
281 	if (mntflags & MS_RDONLY) {
282 		char *p;
283 		/* convert "rw"->"ro" */
284 		if (p = strstr(optbuf, "rw")) {
285 			if (*(p+2) == ',' || *(p+2) == '\0')
286 				*(p+1) = 'o';
287 		}
288 	}
289 
290 	if (optind + 2 != argc)
291 		usage();
292 
293 	mnt.mnt_special = argv[optind];
294 	mnt.mnt_mountp = argv[optind+1];
295 
296 	realpath(argv[optind+1], mount_point);
297 	if (stat(mount_point, &st) == -1)
298 		err(EX_MNT, gettext("could not find mount point %s"),
299 		    mount_point);
300 	if (!S_ISDIR(st.st_mode)) {
301 		errno = ENOTDIR;
302 		err(EX_MNT, gettext("can't mount on %s"), mount_point);
303 	}
304 
305 	/*
306 	 * Fill in mdata defaults.
307 	 */
308 	if (mdata.uid == (uid_t)-1)
309 		mdata.uid = getuid();
310 	if (mdata.gid == (gid_t)-1)
311 		mdata.gid = getgid();
312 	if (mdata.file_mode == 0)
313 		mdata.file_mode = S_IRWXU;
314 	if (mdata.dir_mode == 0) {
315 		mdata.dir_mode = mdata.file_mode;
316 		if (mdata.dir_mode & S_IRUSR)
317 			mdata.dir_mode |= S_IXUSR;
318 		if (mdata.dir_mode & S_IRGRP)
319 			mdata.dir_mode |= S_IXGRP;
320 		if (mdata.dir_mode & S_IROTH)
321 			mdata.dir_mode |= S_IXOTH;
322 	}
323 
324 	ctx->ct_ssn.ssn_owner = SMBM_ANY_OWNER;
325 	if (noprompt)
326 		ctx->ct_flags |= SMBCF_NOPWD;
327 
328 	/*
329 	 * Resolve the server address,
330 	 * setup derived defaults.
331 	 */
332 	error = smb_ctx_resolve(ctx);
333 	if (error)
334 		exit(RET_ERR);
335 
336 	/*
337 	 * Have server, share, etc. from above:
338 	 * smb_ctx_scan_argv, option settings.
339 	 * Get the session and tree.
340 	 */
341 again:
342 	error = smb_ctx_get_ssn(ctx);
343 	if (error == EAUTH && noprompt == 0) {
344 		err2 = smb_get_authentication(ctx);
345 		if (err2 == 0)
346 			goto again;
347 	}
348 	if (error) {
349 		smb_error(gettext("//%s: login failed"),
350 		    error, ctx->ct_fullserver);
351 		exit(RET_ERR);
352 	}
353 
354 	error = smb_ctx_get_tree(ctx);
355 	if (error) {
356 		smb_error(gettext("//%s/%s: tree connect failed"),
357 		    error, ctx->ct_fullserver, ctx->ct_origshare);
358 		exit(RET_ERR);
359 	}
360 
361 	/*
362 	 * Have tree connection, now mount it.
363 	 */
364 	mdata.devfd = ctx->ct_dev_fd;
365 
366 	if (mount(mnt.mnt_special, mnt.mnt_mountp,
367 	    mntflags, fstype, &mdata, sizeof (mdata),
368 	    mnt.mnt_mntopts, MAX_MNTOPT_STR) < 0) {
369 		if (errno != ENOENT) {
370 			err(EX_MNT, gettext("mount_smbfs: %s"),
371 			    mnt.mnt_mountp);
372 		} else {
373 			struct stat sb;
374 			if (stat(mnt.mnt_mountp, &sb) < 0 &&
375 			    errno == ENOENT)
376 				err(EX_MNT, gettext("mount_smbfs: %s"),
377 				    mnt.mnt_mountp);
378 			else
379 				err(EX_MNT, gettext("mount_smbfs: %s"),
380 				    mnt.mnt_special);
381 		}
382 	}
383 
384 	smb_ctx_free(ctx);
385 	return (0);
386 }
387 
388 #define	bad(val) (val == NULL || !isdigit(*val))
389 
390 int
391 setsubopt(smb_ctx_t *ctx, struct smbfs_args *mdatap, char *subopt)
392 {
393 	char *equals, *optarg;
394 	struct passwd *pwd;
395 	struct group *grp;
396 	long val;
397 	int rc = EX_OK;
398 	int index;
399 	char *p;
400 
401 	equals = strchr(subopt, '=');
402 	if (equals) {
403 		*equals = '\0';
404 		optarg = equals + 1;
405 	} else
406 		optarg = NULL;
407 
408 	for (index = 0; optlist[index] != NULL; index++) {
409 		if (strcmp(subopt, optlist[index]) == 0)
410 			break;
411 	}
412 
413 	/*
414 	 * Note: if the option was unknown, index will
415 	 * point to the NULL at the end of optlist[],
416 	 * and we'll take the switch default.
417 	 */
418 
419 	switch (index) {
420 
421 	case OPT_ACL:
422 	case OPT_NOACL:
423 		/* Some of our tests use this. */
424 		if (enable_noacl_option == 0)
425 			goto badopt;
426 		/* fallthrough */
427 	case OPT_SUID:
428 	case OPT_NOSUID:
429 	case OPT_DEVICES:
430 	case OPT_NODEVICES:
431 	case OPT_SETUID:
432 	case OPT_NOSETUID:
433 	case OPT_EXEC:
434 	case OPT_NOEXEC:
435 	case OPT_XATTR:
436 	case OPT_NOXATTR:
437 		/*
438 		 * These options are handled via the
439 		 * generic option string mechanism.
440 		 * None of these take an optarg.
441 		 */
442 		if (optarg != NULL)
443 			goto badval;
444 		(void) strlcat(optbuf, ",", sizeof (optbuf));
445 		if (strlcat(optbuf, subopt, sizeof (optbuf)) >=
446 		    sizeof (optbuf)) {
447 			if (!qflg)
448 				warnx(gettext("option string too long"));
449 			rc = EX_OPT;
450 		}
451 		break;
452 
453 	/*
454 	 * OPT_RO, OPT_RW, are actually generic too,
455 	 * but we use the mntflags for these, and
456 	 * then update the options string later.
457 	 */
458 	case OPT_RO:
459 		mntflags |= MS_RDONLY;
460 		break;
461 	case OPT_RW:
462 		mntflags &= ~MS_RDONLY;
463 		break;
464 
465 	/*
466 	 * NFS-derived options for attribute cache
467 	 * handling (disable, set min/max timeouts)
468 	 */
469 	case OPT_NOAC:
470 		mdatap->flags |= SMBFS_MF_NOAC;
471 		break;
472 
473 	case OPT_ACTIMEO:
474 		errno = 0;
475 		val = strtol(optarg, &p, 10);
476 		if (errno || *p != 0)
477 			goto badval;
478 		mdatap->acdirmin = mdatap->acregmin = val;
479 		mdatap->acdirmax = mdatap->acregmax = val;
480 		mdatap->flags |= SMBFS_MF_ACDIRMAX;
481 		mdatap->flags |= SMBFS_MF_ACREGMAX;
482 		mdatap->flags |= SMBFS_MF_ACDIRMIN;
483 		mdatap->flags |= SMBFS_MF_ACREGMIN;
484 		break;
485 
486 	case OPT_ACREGMIN:
487 		errno = 0;
488 		val = strtol(optarg, &p, 10);
489 		if (errno || *p != 0)
490 			goto badval;
491 		mdatap->acregmin = val;
492 		mdatap->flags |= SMBFS_MF_ACREGMIN;
493 		break;
494 
495 	case OPT_ACREGMAX:
496 		errno = 0;
497 		val = strtol(optarg, &p, 10);
498 		if (errno || *p != 0)
499 			goto badval;
500 		mdatap->acregmax = val;
501 		mdatap->flags |= SMBFS_MF_ACREGMAX;
502 		break;
503 
504 	case OPT_ACDIRMIN:
505 		errno = 0;
506 		val = strtol(optarg, &p, 10);
507 		if (errno || *p != 0)
508 			goto badval;
509 		mdatap->acdirmin = val;
510 		mdatap->flags |= SMBFS_MF_ACDIRMIN;
511 		break;
512 
513 	case OPT_ACDIRMAX:
514 		errno = 0;
515 		val = strtol(optarg, &p, 10);
516 		if (errno || *p != 0)
517 			goto badval;
518 		mdatap->acdirmax = val;
519 		mdatap->flags |= SMBFS_MF_ACDIRMAX;
520 		break;
521 
522 	/*
523 	 * SMBFS-specific options.  Some of these
524 	 * don't go through the mount system call,
525 	 * but just set libsmbfs options.
526 	 */
527 	case OPT_DOMAIN:
528 		if (smb_ctx_setdomain(ctx, optarg, B_TRUE) != 0)
529 			rc = EX_OPT;
530 		break;
531 
532 	case OPT_USER:
533 		if (smb_ctx_setuser(ctx, optarg, B_TRUE) != 0)
534 			rc = EX_OPT;
535 		break;
536 
537 	case OPT_UID:
538 		pwd = isdigit(optarg[0]) ?
539 		    getpwuid(atoi(optarg)) : getpwnam(optarg);
540 		if (pwd == NULL) {
541 			if (!qflg)
542 				warnx(gettext("unknown user '%s'"), optarg);
543 			rc = EX_OPT;
544 		} else {
545 			mdatap->uid = pwd->pw_uid;
546 		}
547 		break;
548 
549 	case OPT_GID:
550 		grp = isdigit(optarg[0]) ?
551 		    getgrgid(atoi(optarg)) : getgrnam(optarg);
552 		if (grp == NULL) {
553 			if (!qflg)
554 				warnx(gettext("unknown group '%s'"), optarg);
555 			rc = EX_OPT;
556 		} else {
557 			mdatap->gid = grp->gr_gid;
558 		}
559 		break;
560 
561 	case OPT_DIRPERMS:
562 		errno = 0;
563 		val = strtol(optarg, &p, 8);
564 		if (errno || *p != 0)
565 			goto badval;
566 		mdatap->dir_mode = val;
567 		break;
568 
569 	case OPT_FILEPERMS:
570 		errno = 0;
571 		val = strtol(optarg, &p, 8);
572 		if (errno || *p != 0)
573 			goto badval;
574 		mdatap->file_mode = val;
575 		break;
576 
577 	case OPT_NOPROMPT:
578 		noprompt++;
579 		break;
580 
581 	default:
582 	badopt:
583 		if (!qflg)
584 			warnx(gettext("unknown option %s"), subopt);
585 		rc = EX_OPT;
586 		break;
587 
588 	badval:
589 		if (!qflg)
590 			warnx(gettext("invalid value for %s"), subopt);
591 		rc = EX_OPT;
592 		break;
593 	}
594 
595 	/* Undo changes made to subopt */
596 	if (equals)
597 		*equals = '=';
598 
599 	return (rc);
600 }
601 
602 static void
603 usage(void)
604 {
605 	fprintf(stderr, "%s\n",
606 	gettext("usage: mount -F smbfs [-Orq] [-o option[,option]]"
607 	"	//[workgroup;][user[:password]@]server[/share] path"));
608 
609 	exit(EX_OPT);
610 }
611