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