Home | History | Annotate | Download | only in mount
      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