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