#include <sys/param.h>
#include <sys/stat.h>
#include <sys/errno.h>
#include <sys/mount.h>
#include <stdio.h>
#include <string.h>
#include <pwd.h>
#include <grp.h>
#include <unistd.h>
#include <ctype.h>
#include <stdlib.h>
#include <err.h>
#include <sysexits.h>
#include <cflib.h>
#include <Kerberos/KerberosLogin.h>
#include <Kerberos/com_err.h>
#include <netsmb/smb.h>
#include <netsmb/smb_conn.h>
#include <netsmb/smb_lib.h>
#include <fs/smbfs/smbfs.h>
#include "mntopts.h"
static char mount_point[MAXPATHLEN + 1];
static void usage(void);
static struct mntopt mopts[] = {
MOPT_STDOPTS,
{ NULL, 0, 0, 0 }
};
extern KLStatus __KLSetHomeDirectoryAccess (KLBoolean inAllowHomeDirectoryAccess);
static void my_com_err_proc(const char *whoami, long int code, const char *format, va_list ap);
int
main(int argc, char *argv[])
{
struct smb_ctx sctx, *ctx = &sctx;
struct smbfs_args mdata;
struct stat st;
#ifdef APPLE
extern void dropsuid();
extern int loadsmbvfs();
#endif
struct vfsconf vfc;
char *next;
int opt, error, mntflags, caseopt;
#ifdef APPLE
dropsuid();
#endif
if (argc == 2) {
if (strcmp(argv[1], "-h") == 0) {
usage();
} else if (strcmp(argv[1], "-v") == 0) {
errx(EX_OK, "version %d.%d.%d", SMBFS_VERSION / 100000,
(SMBFS_VERSION % 10000) / 1000,
(SMBFS_VERSION % 1000) / 100);
}
}
if (argc < 3)
usage();
error = getvfsbyname(SMBFS_VFSNAME, &vfc);
#ifdef APPLE
if (error) {
error = loadsmbvfs();
error = getvfsbyname(SMBFS_VFSNAME, &vfc);
}
#else
if (error && vfsisloadable(SMBFS_VFSNAME)) {
if(vfsload(SMBFS_VFSNAME))
err(EX_OSERR, "vfsload("SMBFS_VFSNAME")");
endvfsent();
error = getvfsbyname(SMBFS_VFSNAME, &vfc);
}
#endif
if (error)
errx(EX_OSERR, "SMB filesystem is not available");
error = smb_lib_init();
if (error)
exit(error);
mntflags = error = 0;
bzero(&mdata, sizeof(mdata));
mdata.uid = mdata.gid = -1;
caseopt = SMB_CS_NONE;
error = smb_ctx_init(ctx, argc, argv, SMBL_SHARE, SMBL_SHARE, SMB_ST_DISK);
if (error)
exit(error);
error = smb_ctx_readrc(ctx);
if (error)
exit(error);
if (smb_rc)
rc_close(smb_rc);
#ifdef APPLE
while ((opt = getopt(argc, argv, STDPARAM_OPT"c:d:f:g:l:n:o:u:w:x:")) != -1) {
#else
while ((opt = getopt(argc, argv, STDPARAM_OPT"c:d:f:g:l:n:o:u:w:")) != -1) {
#endif
switch (opt) {
case STDPARAM_ARGS:
error = smb_ctx_opt(ctx, opt, optarg);
if (error)
exit(error);
break;
case 'u': {
struct passwd *pwd;
pwd = isdigit(optarg[0]) ?
getpwuid(atoi(optarg)) : getpwnam(optarg);
if (pwd == NULL)
errx(EX_NOUSER, "unknown user '%s'", optarg);
mdata.uid = pwd->pw_uid;
break;
}
case 'g': {
struct group *grp;
grp = isdigit(optarg[0]) ?
getgrgid(atoi(optarg)) : getgrnam(optarg);
if (grp == NULL)
errx(EX_NOUSER, "unknown group '%s'", optarg);
mdata.gid = grp->gr_gid;
break;
}
case 'd':
errno = 0;
mdata.dir_mode = strtol(optarg, &next, 8);
if (errno || *next != 0)
errx(EX_DATAERR, "invalid value for directory mode");
break;
case 'f':
errno = 0;
mdata.file_mode = strtol(optarg, &next, 8);
if (errno || *next != 0)
errx(EX_DATAERR, "invalid value for file mode");
break;
case '?':
usage();
case 'n': {
char *inp, *nsp;
nsp = inp = optarg;
while ((nsp = strsep(&inp, ",;:")) != NULL) {
if (strcasecmp(nsp, "LONG") == 0)
mdata.flags |= SMBFS_MOUNT_NO_LONG;
else
errx(EX_DATAERR, "unknown suboption '%s'", nsp);
}
break;
};
case 'o':
getmntopts(optarg, mopts, &mntflags, 0);
break;
case 'c':
switch (optarg[0]) {
case 'l':
caseopt |= SMB_CS_LOWER;
break;
case 'u':
caseopt |= SMB_CS_UPPER;
break;
default:
errx(EX_DATAERR, "invalid suboption '%c' for -c",
optarg[0]);
}
break;
#ifdef APPLE
case 'x':
if (!isdigit(optarg[0]))
errx(EX_USAGE, "non-numeric mount count '%s'",
optarg);
ctx->ct_maxxxx = atoi(optarg);
ctx->ct_flags |= SMBCF_XXX;
ctx->ct_minlevel = SMBL_VC;
ctx->ct_maxlevel = SMBL_VC;
if (mdata.file_mode == 0)
mdata.file_mode = S_IRWXU;
if (mdata.dir_mode == 0)
mdata.dir_mode = S_IRWXU;
break;
#endif
default:
usage();
}
}
if (optind == argc - 2)
optind++;
if (optind != argc - 1)
usage();
realpath(argv[optind], mount_point);
if (stat(mount_point, &st) == -1)
err(EX_OSERR, "could not find mount point %s", mount_point);
if (!S_ISDIR(st.st_mode)) {
errno = ENOTDIR;
err(EX_OSERR, "can't mount on %s", mount_point);
}
if (mdata.uid == (uid_t)-1)
mdata.uid = st.st_uid;
if (mdata.gid == (gid_t)-1)
mdata.gid = st.st_gid;
if (mdata.file_mode == 0 )
mdata.file_mode = st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO);
if (mdata.dir_mode == 0) {
mdata.dir_mode = mdata.file_mode;
if (mdata.dir_mode & S_IRUSR)
mdata.dir_mode |= S_IXUSR;
if (mdata.dir_mode & S_IRGRP)
mdata.dir_mode |= S_IXGRP;
if (mdata.dir_mode & S_IROTH)
mdata.dir_mode |= S_IXOTH;
}
if (mntflags & MNT_AUTOMOUNTED) {
__KLSetHomeDirectoryAccess(0);
set_com_err_hook(my_com_err_proc);
}
ctx->ct_ssn.ioc_opt |= SMBVOPT_PRIVATE;
ctx->ct_ssn.ioc_owner = ctx->ct_sh.ioc_owner = st.st_uid;
ctx->ct_ssn.ioc_group = ctx->ct_sh.ioc_group = mdata.gid;
opt = 0;
if (mdata.dir_mode & S_IXGRP)
opt |= SMBM_EXECGRP;
if (mdata.dir_mode & S_IXOTH)
opt |= SMBM_EXECOTH;
ctx->ct_ssn.ioc_rights |= opt;
ctx->ct_sh.ioc_rights |= opt;
#ifdef APPLE
reauth:
#endif
error = smb_ctx_resolve(ctx);
if (error)
exit(error);
#ifdef APPLE
if (!(ctx->ct_flags & SMBCF_XXX)) {
again:
error = smb_ctx_lookup(ctx, SMBL_SHARE, SMBLK_CREATE);
if (error == ENOENT && ctx->ct_origshare) {
strcpy(ctx->ct_sh.ioc_share, ctx->ct_origshare);
free(ctx->ct_origshare);
ctx->ct_origshare = NULL;
goto again;
}
if (ctx->ct_flags & SMBCF_KCFOUND && smb_autherr(error)) {
ctx->ct_ssn.ioc_password[0] = '\0';
goto reauth;
}
}
#else
error = smb_ctx_lookup(ctx, SMBL_SHARE, SMBLK_CREATE);
#endif
if (error)
exit(error);
strcpy(mdata.mount_point, mount_point);
mdata.version = SMBFS_VERSION;
mdata.dev = ctx->ct_fd;
mdata.caseopt = caseopt;
#ifdef APPLE
if (ctx->ct_flags & SMBCF_XXX) {
char **cpp = ctx->ct_xxx;
if (!cpp) {
smb_ctx_done(ctx);
return 0;
}
for ( ; *cpp; cpp++) {
if ((unsigned int)snprintf(mdata.mount_point,
sizeof mdata.mount_point, "%s/%s",
mount_point, *cpp) >=
sizeof mdata.mount_point) {
smb_error("buffer overflow (attack?) on %s", 0,
mdata.mount_point);
continue;
}
error = smb_ctx_setshare(ctx, *cpp, SMB_ST_DISK);
if (error) {
smb_error("x setshare error %d on %s", 0,
error, mdata.mount_point);
continue;
}
lookup:
error = smb_ctx_lookup(ctx, SMBL_SHARE, SMBLK_CREATE);
if (error) {
smb_error("x lookup error: %s", error,
mdata.mount_point);
if (error == ENOENT && ctx->ct_origshare) {
strcpy(ctx->ct_sh.ioc_share, ctx->ct_origshare);
free(ctx->ct_origshare);
ctx->ct_origshare = NULL;
goto lookup;
}
continue;
}
mdata.dev = ctx->ct_fd;
(void)rmdir(mdata.mount_point);
error = mkdir(mdata.mount_point, mdata.dir_mode);
if (error) {
smb_error("x mkdir error: %s", error,
mdata.mount_point);
error = smb_ctx_tdis(ctx);
if (error)
exit(error);
continue;
}
error = mount(SMBFS_VFSNAME, mdata.mount_point,
mntflags, (void*)&mdata);
if (error) {
smb_error("mount mount error: %s", error,
mdata.mount_point);
error = smb_ctx_tdis(ctx);
if (error)
exit(error);
continue;
}
}
cpp++;
free(*cpp);
free(ctx->ct_xxx);
ctx->ct_xxx = NULL;
smb_ctx_done(ctx);
return error;
}
#endif
error = mount(SMBFS_VFSNAME, mdata.mount_point, mntflags,
(void*)&mdata);
#ifdef APPLE
if (ctx->ct_flags & SMBCF_KCFOUND && smb_autherr(error)) {
ctx->ct_ssn.ioc_password[0] = '\0';
goto reauth;
}
if (!error)
smb_save2keychain(ctx);
#endif
smb_ctx_done(ctx);
if (error) {
smb_error("mount error: %s", error, mdata.mount_point);
exit(errno);
}
return 0;
}
static void
usage(void)
{
fprintf(stderr, "%s\n",
"usage: mount_smbfs [-Nh]"
#ifndef APPLE
" [-E cs1:cs2]"
#endif
" [-I host]"
#ifndef APPLE
" [-L locale]"
#endif
"\n"
" [-M cmode[/smode]] [-O cuid[:cgid]/suid[:sgid]]\n"
" [-R retrycount] [-T timeout]\n"
" [-U user] [-W workgroup]"
#ifndef APPLE
" [-c case]"
#endif
"\n"
" [-d mode] [-f mode] [-g gid] [-n long] [-u uid]\n"
" //"
#ifdef APPLE
"[workgroup;][user[:password]@]server[/share]"
#else
"[user@]server/share"
#endif
" path");
exit (EX_USAGE);
}
static void
my_com_err_proc(const char *whoami, long int code, const char *format, va_list ap)
{
fprintf(stderr, "%s: ", whoami);
if (code)
fprintf(stderr, "error code %ld - ", code);
vfprintf(stderr, format, ap);
}