2 Unix SMB/CIFS implementation.
4 Copyright (C) Andrew Tridgell 1999
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include <asm/types.h>
27 #include <linux/smb_fs.h>
29 extern BOOL in_client;
30 extern pstring user_socket_options;
32 static pstring credentials;
33 static pstring my_netbios_name;
34 static pstring password;
35 static pstring username;
36 static pstring workgroup;
37 static pstring mpoint;
38 static pstring service;
39 static pstring options;
41 static struct in_addr dest_ip;
43 static int smb_port = 0;
45 static uid_t mount_uid;
46 static gid_t mount_gid;
48 static unsigned mount_fmask;
49 static unsigned mount_dmask;
51 static void usage(void);
53 static void exit_parent(int sig)
55 /* parent simply exits when child says go... */
59 static void daemonize(void)
64 signal( SIGTERM, exit_parent );
66 if ((child_pid = sys_fork()) < 0) {
67 DEBUG(0,("could not fork\n"));
72 j = waitpid( child_pid, &status, 0 );
74 if( EINTR == errno ) {
82 /* If we get here - the child exited with some error status */
83 if (WIFSIGNALED(status))
84 exit(128 + WTERMSIG(status));
86 exit(WEXITSTATUS(status));
89 signal( SIGTERM, SIG_DFL );
93 static void close_our_files(int client_fd)
98 getrlimit(RLIMIT_NOFILE,&limits);
99 for (i = 0; i< limits.rlim_max; i++) {
106 static void usr1_handler(int x)
112 /*****************************************************
113 return a connection to a server
114 *******************************************************/
115 static struct cli_state *do_connection(char *the_service)
118 struct nmb_name called, calling;
124 if (the_service[0] != '\\' || the_service[1] != '\\') {
129 pstrcpy(server, the_service+2);
130 share = strchr_m(server,'\\');
140 make_nmb_name(&calling, my_netbios_name, 0x0);
141 make_nmb_name(&called , server, 0x20);
145 if (have_ip) ip = dest_ip;
147 /* have to open a new connection */
148 if (!(c=cli_initialise(NULL)) || (cli_set_port(c, smb_port) != smb_port) ||
149 !cli_connect(c, server_n, &ip)) {
150 DEBUG(0,("%d: Connection to %s failed\n", sys_getpid(), server_n));
157 /* SPNEGO doesn't work till we get NTSTATUS error support */
158 c->use_spnego = False;
160 /* The kernel doesn't yet know how to sign it's packets */
161 c->sign_info->allow_smb_signing = False;
163 if (!cli_session_request(c, &calling, &called)) {
165 DEBUG(0,("%d: session request to %s failed (%s)\n",
166 sys_getpid(), called.name, cli_errstr(c)));
168 if ((p=strchr_m(called.name, '.'))) {
172 if (strcmp(called.name, "*SMBSERVER")) {
173 make_nmb_name(&called , "*SMBSERVER", 0x20);
179 DEBUG(4,("%d: session request ok\n", sys_getpid()));
181 if (!cli_negprot(c)) {
182 DEBUG(0,("%d: protocol negotiation failed\n", sys_getpid()));
188 char *pass = getpass("Password: ");
190 pstrcpy(password, pass);
194 /* This should be right for current smbfs. Future versions will support
195 large files as well as unicode and oplocks. */
196 c->capabilities &= ~(CAP_UNICODE | CAP_LARGE_FILES | CAP_NT_SMBS |
197 CAP_NT_FIND | CAP_STATUS32 | CAP_LEVEL_II_OPLOCKS);
198 c->force_dos_errors = True;
199 if (!cli_session_setup(c, username,
200 password, strlen(password),
201 password, strlen(password),
203 /* if a password was not supplied then try again with a
205 if (password[0] || !username[0] ||
206 !cli_session_setup(c, "", "", 0, "", 0, workgroup)) {
207 DEBUG(0,("%d: session setup failed: %s\n",
208 sys_getpid(), cli_errstr(c)));
212 DEBUG(0,("Anonymous login successful\n"));
215 DEBUG(4,("%d: session setup ok\n", sys_getpid()));
217 if (!cli_send_tconX(c, share, "?????",
218 password, strlen(password)+1)) {
219 DEBUG(0,("%d: tree connect failed: %s\n",
220 sys_getpid(), cli_errstr(c)));
225 DEBUG(4,("%d: tconx ok\n", sys_getpid()));
233 /****************************************************************************
234 unmount smbfs (this is a bailout routine to clean up if a reconnect fails)
235 Code blatently stolen from smbumount.c
237 ****************************************************************************/
238 static void smb_umount(char *mount_point)
246 This routine only gets called to the scene of a disaster
247 to shoot the survivors... A connection that was working
248 has now apparently failed. We have an active mount point
249 (presumably) that we need to dump. If we get errors along
250 the way - make some noise, but we are already turning out
251 the lights to exit anyways...
253 if (umount(mount_point) != 0) {
254 DEBUG(0,("%d: Could not umount %s: %s\n",
255 sys_getpid(), mount_point, strerror(errno)));
259 if ((fd = open(MOUNTED"~", O_RDWR|O_CREAT|O_EXCL, 0600)) == -1) {
260 DEBUG(0,("%d: Can't get "MOUNTED"~ lock file", sys_getpid()));
266 if ((mtab = setmntent(MOUNTED, "r")) == NULL) {
267 DEBUG(0,("%d: Can't open " MOUNTED ": %s\n",
268 sys_getpid(), strerror(errno)));
272 #define MOUNTED_TMP MOUNTED".tmp"
274 if ((new_mtab = setmntent(MOUNTED_TMP, "w")) == NULL) {
275 DEBUG(0,("%d: Can't open " MOUNTED_TMP ": %s\n",
276 sys_getpid(), strerror(errno)));
281 while ((mnt = getmntent(mtab)) != NULL) {
282 if (strcmp(mnt->mnt_dir, mount_point) != 0) {
283 addmntent(new_mtab, mnt);
289 if (fchmod (fileno (new_mtab), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) {
290 DEBUG(0,("%d: Error changing mode of %s: %s\n",
291 sys_getpid(), MOUNTED_TMP, strerror(errno)));
297 if (rename(MOUNTED_TMP, MOUNTED) < 0) {
298 DEBUG(0,("%d: Cannot rename %s to %s: %s\n",
299 sys_getpid(), MOUNTED, MOUNTED_TMP, strerror(errno)));
303 if (unlink(MOUNTED"~") == -1) {
304 DEBUG(0,("%d: Can't remove "MOUNTED"~", sys_getpid()));
311 * Call the smbfs ioctl to install a connection socket,
312 * then wait for a signal to reconnect. Note that we do
313 * not exit after open_sockets() or send_login() errors,
314 * as the smbfs mount would then have no way to recover.
316 static void send_fs_socket(char *the_service, char *mount_point, struct cli_state *c)
318 int fd, closed = 0, res = 1;
319 pid_t parentpid = getppid();
320 struct smb_conn_opt conn_options;
322 memset(&conn_options, 0, sizeof(conn_options));
325 if ((fd = open(mount_point, O_RDONLY)) < 0) {
326 DEBUG(0,("mount.smbfs[%d]: can't open %s\n",
327 sys_getpid(), mount_point));
331 conn_options.fd = c->fd;
332 conn_options.protocol = c->protocol;
333 conn_options.case_handling = SMB_CASE_DEFAULT;
334 conn_options.max_xmit = c->max_xmit;
335 conn_options.server_uid = c->vuid;
336 conn_options.tid = c->cnum;
337 conn_options.secmode = c->sec_mode;
338 conn_options.rawmode = 0;
339 conn_options.sesskey = c->sesskey;
340 conn_options.maxraw = 0;
341 conn_options.capabilities = c->capabilities;
342 conn_options.serverzone = c->serverzone/60;
344 res = ioctl(fd, SMB_IOC_NEWCONN, &conn_options);
346 DEBUG(0,("mount.smbfs[%d]: ioctl failed, res=%d\n",
353 /* Ok... We are going to kill the parent. Now
354 is the time to break the process group... */
356 /* Send a signal to the parent to terminate */
357 kill(parentpid, SIGTERM);
363 /* This looks wierd but we are only closing the userspace
364 side, the connection has already been passed to smbfs and
365 it has increased the usage count on the socket.
367 If we don't do this we will "leak" sockets and memory on
368 each reconnection we have to make. */
373 /* redirect stdout & stderr since we can't know that
374 the library functions we use are using DEBUG. */
375 if ( (fd = open("/dev/null", O_WRONLY)) < 0)
376 DEBUG(2,("mount.smbfs: can't open /dev/null\n"));
379 dup2(fd, STDOUT_FILENO);
380 dup2(fd, STDERR_FILENO);
384 /* here we are no longer interactive */
385 set_remote_machine_name("smbmount"); /* sneaky ... */
386 setup_logging("mount.smbfs", False);
388 DEBUG(0, ("mount.smbfs: entering daemon mode for service %s, pid=%d\n", the_service, sys_getpid()));
393 /* Wait for a signal from smbfs ... but don't continue
394 until we actually get a new connection. */
396 CatchSignal(SIGUSR1, &usr1_handler);
398 DEBUG(2,("mount.smbfs[%d]: got signal, getting new socket\n", sys_getpid()));
399 c = do_connection(the_service);
403 smb_umount(mount_point);
404 DEBUG(2,("mount.smbfs[%d]: exit\n", sys_getpid()));
412 static void init_mount(void)
414 char mount_point[MAXPATHLEN+1];
421 if (realpath(mpoint, mount_point) == NULL) {
422 fprintf(stderr, "Could not resolve mount point %s\n", mpoint);
427 c = do_connection(service);
429 fprintf(stderr,"SMB connection failed\n");
434 Set up to return as a daemon child and wait in the parent
435 until the child say it's ready...
439 pstrcpy(svc2, service);
440 string_replace(svc2, '\\','/');
441 string_replace(svc2, ' ','_');
443 memset(args, 0, sizeof(args[0])*20);
446 args[i++] = "smbmnt";
448 args[i++] = mount_point;
456 slprintf(tmp, sizeof(tmp)-1, "%d", mount_uid);
458 args[i++] = smb_xstrdup(tmp);
461 slprintf(tmp, sizeof(tmp)-1, "%d", mount_gid);
463 args[i++] = smb_xstrdup(tmp);
466 slprintf(tmp, sizeof(tmp)-1, "0%o", mount_fmask);
468 args[i++] = smb_xstrdup(tmp);
471 slprintf(tmp, sizeof(tmp)-1, "0%o", mount_dmask);
473 args[i++] = smb_xstrdup(tmp);
480 if (sys_fork() == 0) {
483 asprintf(&smbmnt_path, "%s/smbmnt", dyn_BINDIR);
485 if (file_exist(smbmnt_path, NULL)) {
486 execv(smbmnt_path, args);
488 "smbfs/init_mount: execv of %s failed. Error was %s.",
489 smbmnt_path, strerror(errno));
491 execvp("smbmnt", args);
493 "smbfs/init_mount: execv of %s failed. Error was %s.",
494 "smbmnt", strerror(errno));
500 if (waitpid(-1, &status, 0) == -1) {
501 fprintf(stderr,"waitpid failed: Error was %s", strerror(errno) );
502 /* FIXME: do some proper error handling */
506 if (WIFEXITED(status) && WEXITSTATUS(status) != 0) {
507 fprintf(stderr,"smbmnt failed: %d\n", WEXITSTATUS(status));
508 /* FIXME: do some proper error handling */
512 /* Ok... This is the rubicon for that mount point... At any point
513 after this, if the connections fail and can not be reconstructed
514 for any reason, we will have to unmount the mount point. There
515 is no exit from the next call...
517 send_fs_socket(service, mount_point, c);
521 /****************************************************************************
522 get a password from a a file or file descriptor
523 exit on failure (from smbclient, move to libsmb or shared .c file?)
524 ****************************************************************************/
525 static void get_password_file(void)
529 BOOL close_it = False;
533 if ((p = getenv("PASSWD_FD")) != NULL) {
534 pstrcpy(spec, "descriptor ");
536 sscanf(p, "%d", &fd);
538 } else if ((p = getenv("PASSWD_FILE")) != NULL) {
539 fd = sys_open(p, O_RDONLY, 0);
542 fprintf(stderr, "Error opening PASSWD_FILE %s: %s\n",
543 spec, strerror(errno));
549 for(p = pass, *p = '\0'; /* ensure that pass is null-terminated */
550 p && p - pass < sizeof(pass);) {
551 switch (read(fd, p, 1)) {
553 if (*p != '\n' && *p != '\0') {
554 *++p = '\0'; /* advance p, and null-terminate pass */
559 *p = '\0'; /* null-terminate it, just in case... */
560 p = NULL; /* then force the loop condition to become false */
563 fprintf(stderr, "Error reading password from file %s: %s\n",
564 spec, "empty password\n");
569 fprintf(stderr, "Error reading password from file %s: %s\n",
570 spec, strerror(errno));
574 pstrcpy(password, pass);
579 /****************************************************************************
580 get username and password from a credentials file
581 exit on failure (from smbclient, move to libsmb or shared .c file?)
582 ****************************************************************************/
583 static void read_credentials_file(char *filename)
588 char *ptr, *val, *param;
590 if ((auth=sys_fopen(filename, "r")) == NULL)
592 /* fail if we can't open the credentials file */
593 DEBUG(0,("ERROR: Unable to open credentials file!\n"));
599 /* get a line from the file */
600 if (!fgets (buf, sizeof(buf), auth))
604 if ((len) && (buf[len-1]=='\n'))
612 /* break up the line into parameter & value.
613 will need to eat a little whitespace possibly */
615 if (!(ptr = strchr (buf, '=')))
620 /* eat leading white space */
621 while ((*val!='\0') && ((*val==' ') || (*val=='\t')))
624 if (strwicmp("password", param) == 0)
626 pstrcpy(password, val);
629 else if (strwicmp("username", param) == 0)
630 pstrcpy(username, val);
632 memset(buf, 0, sizeof(buf));
638 /****************************************************************************
640 ****************************************************************************/
641 static void usage(void)
643 printf("Usage: mount.smbfs service mountpoint [-o options,...]\n");
645 printf("Version %s\n\n",VERSION);
649 username=<arg> SMB username\n\
650 password=<arg> SMB password\n\
651 credentials=<filename> file with username/password\n\
652 netbiosname=<arg> source NetBIOS name\n\
653 uid=<arg> mount uid or username\n\
654 gid=<arg> mount gid or groupname\n\
655 port=<arg> remote SMB port number\n\
656 fmask=<arg> file umask\n\
657 dmask=<arg> directory umask\n\
658 debug=<arg> debug level\n\
659 ip=<arg> destination host or IP address\n\
660 workgroup=<arg> workgroup on destination\n\
661 sockopt=<arg> TCP socket options\n\
662 scope=<arg> NetBIOS scope\n\
663 iocharset=<arg> Linux charset (iso8859-1, utf8)\n\
664 codepage=<arg> server codepage (cp850)\n\
665 ttl=<arg> dircache time to live\n\
666 guest don't prompt for a password\n\
667 ro mount read-only\n\
668 rw mount read-write\n\
670 This command is designed to be run from within /bin/mount by giving\n\
671 the option '-t smbfs'. For example:\n\
672 mount -t smbfs -o username=tridge,password=foobar //fjall/test /data/test\n\
677 /****************************************************************************
678 Argument parsing for mount.smbfs interface
679 mount will call us like this:
680 mount.smbfs device mountpoint -o <options>
682 <options> is never empty, containing at least rw or ro
683 ****************************************************************************/
684 static void parse_mount_smb(int argc, char **argv)
693 /* FIXME: This function can silently fail if the arguments are
694 * not in the expected order.
696 > The arguments syntax of smbmount 2.2.3a (smbfs of Debian stable)
697 > requires that one gives "-o" before further options like username=...
698 > . Without -o, the username=.. setting is *silently* ignored. I've
699 > spent about an hour trying to find out why I couldn't log in now..
704 if (argc < 2 || argv[1][0] == '-') {
709 pstrcpy(service, argv[1]);
710 pstrcpy(mpoint, argv[2]);
712 /* Convert any '/' characters in the service name to
714 string_replace(service, '/','\\');
718 opt = getopt(argc, argv, "o:");
727 * option parsing from nfsmount.c (util-linux-2.9u)
729 for (opts = strtok(optarg, ","); opts; opts = strtok(NULL, ",")) {
730 DEBUG(3, ("opts: %s\n", opts));
731 if ((opteq = strchr_m(opts, '='))) {
732 val = atoi(opteq + 1);
735 if (!strcmp(opts, "username") ||
736 !strcmp(opts, "logon")) {
738 pstrcpy(username,opteq+1);
739 if ((lp=strchr_m(username,'%'))) {
741 pstrcpy(password,lp+1);
743 memset(strchr_m(opteq+1,'%')+1,'X',strlen(password));
745 if ((lp=strchr_m(username,'/'))) {
747 pstrcpy(workgroup,lp+1);
749 } else if(!strcmp(opts, "passwd") ||
750 !strcmp(opts, "password")) {
751 pstrcpy(password,opteq+1);
753 memset(opteq+1,'X',strlen(password));
754 } else if(!strcmp(opts, "credentials")) {
755 pstrcpy(credentials,opteq+1);
756 } else if(!strcmp(opts, "netbiosname")) {
757 pstrcpy(my_netbios_name,opteq+1);
758 } else if(!strcmp(opts, "uid")) {
759 mount_uid = nametouid(opteq+1);
760 } else if(!strcmp(opts, "gid")) {
761 mount_gid = nametogid(opteq+1);
762 } else if(!strcmp(opts, "port")) {
764 } else if(!strcmp(opts, "fmask")) {
765 mount_fmask = strtol(opteq+1, NULL, 8);
766 } else if(!strcmp(opts, "dmask")) {
767 mount_dmask = strtol(opteq+1, NULL, 8);
768 } else if(!strcmp(opts, "debug")) {
770 } else if(!strcmp(opts, "ip")) {
771 dest_ip = *interpret_addr2(opteq+1);
772 if (is_zero_ip(dest_ip)) {
773 fprintf(stderr,"Can't resolve address %s\n", opteq+1);
777 } else if(!strcmp(opts, "workgroup")) {
778 pstrcpy(workgroup,opteq+1);
779 } else if(!strcmp(opts, "sockopt")) {
780 pstrcpy(user_socket_options,opteq+1);
781 } else if(!strcmp(opts, "scope")) {
782 set_global_scope(opteq+1);
784 slprintf(p, sizeof(pstring) - (p - options) - 1, "%s=%s,", opts, opteq+1);
789 if(!strcmp(opts, "nocaps")) {
790 fprintf(stderr, "Unhandled option: %s\n", opteq+1);
792 } else if(!strcmp(opts, "guest")) {
795 } else if(!strcmp(opts, "rw")) {
797 } else if(!strcmp(opts, "ro")) {
800 strncpy(p, opts, sizeof(pstring) - (p - options) - 1);
814 *(p-1) = 0; /* remove trailing , */
815 DEBUG(3,("passthrough options '%s'\n", options));
819 /****************************************************************************
821 ****************************************************************************/
822 int main(int argc,char *argv[])
830 /* here we are interactive, even if run from autofs */
831 setup_logging("mount.smbfs",True);
833 #if 0 /* JRA - Urban says not needed ? */
834 /* CLI_FORCE_ASCII=false makes smbmount negotiate unicode. The default
835 is to not announce any unicode capabilities as current smbfs does
837 p = getenv("CLI_FORCE_ASCII");
838 if (p && !strcmp(p, "false"))
839 unsetenv("CLI_FORCE_ASCII");
841 setenv("CLI_FORCE_ASCII", "true", 1);
844 in_client = True; /* Make sure that we tell lp_load we are */
846 if (getenv("USER")) {
847 pstrcpy(username,getenv("USER"));
849 if ((p=strchr_m(username,'%'))) {
851 pstrcpy(password,p+1);
853 memset(strchr_m(getenv("USER"),'%')+1,'X',strlen(password));
858 if (getenv("PASSWD")) {
859 pstrcpy(password,getenv("PASSWD"));
863 if (getenv("PASSWD_FD") || getenv("PASSWD_FILE")) {
868 if (*username == 0 && getenv("LOGNAME")) {
869 pstrcpy(username,getenv("LOGNAME"));
872 if (!lp_load(dyn_CONFIGFILE,True,False,False)) {
873 fprintf(stderr, "Can't load %s - run testparm to debug it\n",
877 parse_mount_smb(argc, argv);
879 if (*credentials != 0) {
880 read_credentials_file(credentials);
883 DEBUG(3,("mount.smbfs started (version %s)\n", VERSION));
885 if (*workgroup == 0) {
886 pstrcpy(workgroup,lp_workgroup());
890 if (!*my_netbios_name) {
891 pstrcpy(my_netbios_name, myhostname());
893 strupper(my_netbios_name);