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