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;
31 extern BOOL append_log;
33 static pstring credentials;
34 static pstring my_netbios_name;
35 static pstring password;
36 static pstring username;
37 static pstring workgroup;
38 static pstring mpoint;
39 static pstring service;
40 static pstring options;
42 static struct in_addr dest_ip;
44 static int smb_port = 0;
46 static uid_t mount_uid;
47 static gid_t mount_gid;
49 static unsigned mount_fmask;
50 static unsigned mount_dmask;
52 static void usage(void);
54 static void exit_parent(int sig)
56 /* parent simply exits when child says go... */
60 static void daemonize(void)
65 signal( SIGTERM, exit_parent );
67 if ((child_pid = sys_fork()) < 0) {
68 DEBUG(0,("could not fork\n"));
73 j = waitpid( child_pid, &status, 0 );
75 if( EINTR == errno ) {
82 /* If we get here - the child exited with some error status */
86 signal( SIGTERM, SIG_DFL );
90 static void close_our_files(int client_fd)
95 getrlimit(RLIMIT_NOFILE,&limits);
96 for (i = 0; i< limits.rlim_max; i++) {
103 static void usr1_handler(int x)
109 /*****************************************************
110 return a connection to a server
111 *******************************************************/
112 static struct cli_state *do_connection(char *the_service)
115 struct nmb_name called, calling;
121 if (the_service[0] != '\\' || the_service[1] != '\\') {
126 pstrcpy(server, the_service+2);
127 share = strchr_m(server,'\\');
137 make_nmb_name(&calling, my_netbios_name, 0x0);
138 make_nmb_name(&called , server, 0x20);
142 if (have_ip) ip = dest_ip;
144 /* have to open a new connection */
145 if (!(c=cli_initialise(NULL)) || (cli_set_port(c, smb_port) != smb_port) ||
146 !cli_connect(c, server_n, &ip)) {
147 DEBUG(0,("%d: Connection to %s failed\n", sys_getpid(), server_n));
154 /* SPNEGO doesn't work till we get NTSTATUS error support */
155 c->use_spnego = False;
157 if (!cli_session_request(c, &calling, &called)) {
159 DEBUG(0,("%d: session request to %s failed (%s)\n",
160 sys_getpid(), called.name, cli_errstr(c)));
162 if ((p=strchr_m(called.name, '.'))) {
166 if (strcmp(called.name, "*SMBSERVER")) {
167 make_nmb_name(&called , "*SMBSERVER", 0x20);
173 DEBUG(4,("%d: session request ok\n", sys_getpid()));
175 if (!cli_negprot(c)) {
176 DEBUG(0,("%d: protocol negotiation failed\n", sys_getpid()));
182 char *pass = getpass("Password: ");
184 pstrcpy(password, pass);
188 /* This should be right for current smbfs. Future versions will support
189 large files as well as unicode and oplocks. */
190 c->capabilities &= ~(CAP_UNICODE | CAP_LARGE_FILES | CAP_NT_SMBS |
191 CAP_NT_FIND | CAP_STATUS32 | CAP_LEVEL_II_OPLOCKS);
192 c->force_dos_errors = True;
193 if (!cli_session_setup(c, username,
194 password, strlen(password),
195 password, strlen(password),
197 /* if a password was not supplied then try again with a
199 if (password[0] || !username[0] ||
200 !cli_session_setup(c, "", "", 0, "", 0, workgroup)) {
201 DEBUG(0,("%d: session setup failed: %s\n",
202 sys_getpid(), cli_errstr(c)));
206 DEBUG(0,("Anonymous login successful\n"));
209 DEBUG(4,("%d: session setup ok\n", sys_getpid()));
211 if (!cli_send_tconX(c, share, "?????",
212 password, strlen(password)+1)) {
213 DEBUG(0,("%d: tree connect failed: %s\n",
214 sys_getpid(), cli_errstr(c)));
219 DEBUG(4,("%d: tconx ok\n", sys_getpid()));
227 /****************************************************************************
228 unmount smbfs (this is a bailout routine to clean up if a reconnect fails)
229 Code blatently stolen from smbumount.c
231 ****************************************************************************/
232 static void smb_umount(char *mount_point)
240 This routine only gets called to the scene of a disaster
241 to shoot the survivors... A connection that was working
242 has now apparently failed. We have an active mount point
243 (presumably) that we need to dump. If we get errors along
244 the way - make some noise, but we are already turning out
245 the lights to exit anyways...
247 if (umount(mount_point) != 0) {
248 DEBUG(0,("%d: Could not umount %s: %s\n",
249 sys_getpid(), mount_point, strerror(errno)));
253 if ((fd = open(MOUNTED"~", O_RDWR|O_CREAT|O_EXCL, 0600)) == -1) {
254 DEBUG(0,("%d: Can't get "MOUNTED"~ lock file", sys_getpid()));
260 if ((mtab = setmntent(MOUNTED, "r")) == NULL) {
261 DEBUG(0,("%d: Can't open " MOUNTED ": %s\n",
262 sys_getpid(), strerror(errno)));
266 #define MOUNTED_TMP MOUNTED".tmp"
268 if ((new_mtab = setmntent(MOUNTED_TMP, "w")) == NULL) {
269 DEBUG(0,("%d: Can't open " MOUNTED_TMP ": %s\n",
270 sys_getpid(), strerror(errno)));
275 while ((mnt = getmntent(mtab)) != NULL) {
276 if (strcmp(mnt->mnt_dir, mount_point) != 0) {
277 addmntent(new_mtab, mnt);
283 if (fchmod (fileno (new_mtab), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) {
284 DEBUG(0,("%d: Error changing mode of %s: %s\n",
285 sys_getpid(), MOUNTED_TMP, strerror(errno)));
291 if (rename(MOUNTED_TMP, MOUNTED) < 0) {
292 DEBUG(0,("%d: Cannot rename %s to %s: %s\n",
293 sys_getpid(), MOUNTED, MOUNTED_TMP, strerror(errno)));
297 if (unlink(MOUNTED"~") == -1) {
298 DEBUG(0,("%d: Can't remove "MOUNTED"~", sys_getpid()));
305 * Call the smbfs ioctl to install a connection socket,
306 * then wait for a signal to reconnect. Note that we do
307 * not exit after open_sockets() or send_login() errors,
308 * as the smbfs mount would then have no way to recover.
310 static void send_fs_socket(char *the_service, char *mount_point, struct cli_state *c)
312 int fd, closed = 0, res = 1;
313 pid_t parentpid = getppid();
314 struct smb_conn_opt conn_options;
316 memset(&conn_options, 0, sizeof(conn_options));
319 if ((fd = open(mount_point, O_RDONLY)) < 0) {
320 DEBUG(0,("mount.smbfs[%d]: can't open %s\n",
321 sys_getpid(), mount_point));
325 conn_options.fd = c->fd;
326 conn_options.protocol = c->protocol;
327 conn_options.case_handling = SMB_CASE_DEFAULT;
328 conn_options.max_xmit = c->max_xmit;
329 conn_options.server_uid = c->vuid;
330 conn_options.tid = c->cnum;
331 conn_options.secmode = c->sec_mode;
332 conn_options.rawmode = 0;
333 conn_options.sesskey = c->sesskey;
334 conn_options.maxraw = 0;
335 conn_options.capabilities = c->capabilities;
336 conn_options.serverzone = c->serverzone/60;
338 res = ioctl(fd, SMB_IOC_NEWCONN, &conn_options);
340 DEBUG(0,("mount.smbfs[%d]: ioctl failed, res=%d\n",
347 /* Ok... We are going to kill the parent. Now
348 is the time to break the process group... */
350 /* Send a signal to the parent to terminate */
351 kill(parentpid, SIGTERM);
357 /* This looks wierd but we are only closing the userspace
358 side, the connection has already been passed to smbfs and
359 it has increased the usage count on the socket.
361 If we don't do this we will "leak" sockets and memory on
362 each reconnection we have to make. */
367 /* redirect stdout & stderr since we can't know that
368 the library functions we use are using DEBUG. */
369 if ( (fd = open("/dev/null", O_WRONLY)) < 0)
370 DEBUG(2,("mount.smbfs: can't open /dev/null\n"));
373 dup2(fd, STDOUT_FILENO);
374 dup2(fd, STDERR_FILENO);
378 /* here we are no longer interactive */
379 set_remote_machine_name("smbmount"); /* sneaky ... */
380 setup_logging("mount.smbfs", False);
383 DEBUG(0, ("mount.smbfs: entering daemon mode for service %s, pid=%d\n", the_service, sys_getpid()));
388 /* Wait for a signal from smbfs ... but don't continue
389 until we actually get a new connection. */
391 CatchSignal(SIGUSR1, &usr1_handler);
393 DEBUG(2,("mount.smbfs[%d]: got signal, getting new socket\n", sys_getpid()));
394 c = do_connection(the_service);
398 smb_umount(mount_point);
399 DEBUG(2,("mount.smbfs[%d]: exit\n", sys_getpid()));
407 static void init_mount(void)
409 char mount_point[MAXPATHLEN+1];
416 if (realpath(mpoint, mount_point) == NULL) {
417 fprintf(stderr, "Could not resolve mount point %s\n", mpoint);
422 c = do_connection(service);
424 fprintf(stderr,"SMB connection failed\n");
429 Set up to return as a daemon child and wait in the parent
430 until the child say it's ready...
434 pstrcpy(svc2, service);
435 string_replace(svc2, '\\','/');
436 string_replace(svc2, ' ','_');
438 memset(args, 0, sizeof(args[0])*20);
441 args[i++] = "smbmnt";
443 args[i++] = mount_point;
451 slprintf(tmp, sizeof(tmp)-1, "%d", mount_uid);
453 args[i++] = smb_xstrdup(tmp);
456 slprintf(tmp, sizeof(tmp)-1, "%d", mount_gid);
458 args[i++] = smb_xstrdup(tmp);
461 slprintf(tmp, sizeof(tmp)-1, "0%o", mount_fmask);
463 args[i++] = smb_xstrdup(tmp);
466 slprintf(tmp, sizeof(tmp)-1, "0%o", mount_dmask);
468 args[i++] = smb_xstrdup(tmp);
475 if (sys_fork() == 0) {
478 asprintf(&smbmnt_path, "%s/smbmnt", dyn_BINDIR);
480 if (file_exist(smbmnt_path, NULL)) {
481 execv(smbmnt_path, args);
483 "smbfs/init_mount: execv of %s failed. Error was %s.",
484 smbmnt_path, strerror(errno));
486 execvp("smbmnt", args);
488 "smbfs/init_mount: execv of %s failed. Error was %s.",
489 "smbmnt", strerror(errno));
495 if (waitpid(-1, &status, 0) == -1) {
496 fprintf(stderr,"waitpid failed: Error was %s", strerror(errno) );
497 /* FIXME: do some proper error handling */
501 if (WIFEXITED(status) && WEXITSTATUS(status) != 0) {
502 fprintf(stderr,"smbmnt failed: %d\n", WEXITSTATUS(status));
503 /* FIXME: do some proper error handling */
507 /* Ok... This is the rubicon for that mount point... At any point
508 after this, if the connections fail and can not be reconstructed
509 for any reason, we will have to unmount the mount point. There
510 is no exit from the next call...
512 send_fs_socket(service, mount_point, c);
516 /****************************************************************************
517 get a password from a a file or file descriptor
518 exit on failure (from smbclient, move to libsmb or shared .c file?)
519 ****************************************************************************/
520 static void get_password_file(void)
524 BOOL close_it = False;
528 if ((p = getenv("PASSWD_FD")) != NULL) {
529 pstrcpy(spec, "descriptor ");
531 sscanf(p, "%d", &fd);
533 } else if ((p = getenv("PASSWD_FILE")) != NULL) {
534 fd = sys_open(p, O_RDONLY, 0);
537 fprintf(stderr, "Error opening PASSWD_FILE %s: %s\n",
538 spec, strerror(errno));
544 for(p = pass, *p = '\0'; /* ensure that pass is null-terminated */
545 p && p - pass < sizeof(pass);) {
546 switch (read(fd, p, 1)) {
548 if (*p != '\n' && *p != '\0') {
549 *++p = '\0'; /* advance p, and null-terminate pass */
554 *p = '\0'; /* null-terminate it, just in case... */
555 p = NULL; /* then force the loop condition to become false */
558 fprintf(stderr, "Error reading password from file %s: %s\n",
559 spec, "empty password\n");
564 fprintf(stderr, "Error reading password from file %s: %s\n",
565 spec, strerror(errno));
569 pstrcpy(password, pass);
574 /****************************************************************************
575 get username and password from a credentials file
576 exit on failure (from smbclient, move to libsmb or shared .c file?)
577 ****************************************************************************/
578 static void read_credentials_file(char *filename)
583 char *ptr, *val, *param;
585 if ((auth=sys_fopen(filename, "r")) == NULL)
587 /* fail if we can't open the credentials file */
588 DEBUG(0,("ERROR: Unable to open credentials file!\n"));
594 /* get a line from the file */
595 if (!fgets (buf, sizeof(buf), auth))
599 if ((len) && (buf[len-1]=='\n'))
607 /* break up the line into parameter & value.
608 will need to eat a little whitespace possibly */
610 if (!(ptr = strchr (buf, '=')))
615 /* eat leading white space */
616 while ((*val!='\0') && ((*val==' ') || (*val=='\t')))
619 if (strwicmp("password", param) == 0)
621 pstrcpy(password, val);
624 else if (strwicmp("username", param) == 0)
625 pstrcpy(username, val);
627 memset(buf, 0, sizeof(buf));
633 /****************************************************************************
635 ****************************************************************************/
636 static void usage(void)
638 printf("Usage: mount.smbfs service mountpoint [-o options,...]\n");
640 printf("Version %s\n\n",VERSION);
644 username=<arg> SMB username\n\
645 password=<arg> SMB password\n\
646 credentials=<filename> file with username/password\n\
647 netbiosname=<arg> source NetBIOS name\n\
648 uid=<arg> mount uid or username\n\
649 gid=<arg> mount gid or groupname\n\
650 port=<arg> remote SMB port number\n\
651 fmask=<arg> file umask\n\
652 dmask=<arg> directory umask\n\
653 debug=<arg> debug level\n\
654 ip=<arg> destination host or IP address\n\
655 workgroup=<arg> workgroup on destination\n\
656 sockopt=<arg> TCP socket options\n\
657 scope=<arg> NetBIOS scope\n\
658 iocharset=<arg> Linux charset (iso8859-1, utf8)\n\
659 codepage=<arg> server codepage (cp850)\n\
660 ttl=<arg> dircache time to live\n\
661 guest don't prompt for a password\n\
662 ro mount read-only\n\
663 rw mount read-write\n\
665 This command is designed to be run from within /bin/mount by giving\n\
666 the option '-t smbfs'. For example:\n\
667 mount -t smbfs -o username=tridge,password=foobar //fjall/test /data/test\n\
672 /****************************************************************************
673 Argument parsing for mount.smbfs interface
674 mount will call us like this:
675 mount.smbfs device mountpoint -o <options>
677 <options> is never empty, containing at least rw or ro
678 ****************************************************************************/
679 static void parse_mount_smb(int argc, char **argv)
686 extern pstring global_scope;
689 if (argc < 2 || argv[1][0] == '-') {
694 pstrcpy(service, argv[1]);
695 pstrcpy(mpoint, argv[2]);
697 /* Convert any '/' characters in the service name to
699 string_replace(service, '/','\\');
703 opt = getopt(argc, argv, "o:");
712 * option parsing from nfsmount.c (util-linux-2.9u)
714 for (opts = strtok(optarg, ","); opts; opts = strtok(NULL, ",")) {
715 DEBUG(3, ("opts: %s\n", opts));
716 if ((opteq = strchr_m(opts, '='))) {
717 val = atoi(opteq + 1);
720 if (!strcmp(opts, "username") ||
721 !strcmp(opts, "logon")) {
723 pstrcpy(username,opteq+1);
724 if ((lp=strchr_m(username,'%'))) {
726 pstrcpy(password,lp+1);
728 memset(strchr_m(opteq+1,'%')+1,'X',strlen(password));
730 if ((lp=strchr_m(username,'/'))) {
732 pstrcpy(workgroup,lp+1);
734 } else if(!strcmp(opts, "passwd") ||
735 !strcmp(opts, "password")) {
736 pstrcpy(password,opteq+1);
738 memset(opteq+1,'X',strlen(password));
739 } else if(!strcmp(opts, "credentials")) {
740 pstrcpy(credentials,opteq+1);
741 } else if(!strcmp(opts, "netbiosname")) {
742 pstrcpy(my_netbios_name,opteq+1);
743 } else if(!strcmp(opts, "uid")) {
744 mount_uid = nametouid(opteq+1);
745 } else if(!strcmp(opts, "gid")) {
746 mount_gid = nametogid(opteq+1);
747 } else if(!strcmp(opts, "port")) {
749 } else if(!strcmp(opts, "fmask")) {
750 mount_fmask = strtol(opteq+1, NULL, 8);
751 } else if(!strcmp(opts, "dmask")) {
752 mount_dmask = strtol(opteq+1, NULL, 8);
753 } else if(!strcmp(opts, "debug")) {
755 } else if(!strcmp(opts, "ip")) {
756 dest_ip = *interpret_addr2(opteq+1);
757 if (is_zero_ip(dest_ip)) {
758 fprintf(stderr,"Can't resolve address %s\n", opteq+1);
762 } else if(!strcmp(opts, "workgroup")) {
763 pstrcpy(workgroup,opteq+1);
764 } else if(!strcmp(opts, "sockopt")) {
765 pstrcpy(user_socket_options,opteq+1);
766 } else if(!strcmp(opts, "scope")) {
767 pstrcpy(global_scope,opteq+1);
769 slprintf(p, sizeof(pstring) - (p - options) - 1, "%s=%s,", opts, opteq+1);
774 if(!strcmp(opts, "nocaps")) {
775 fprintf(stderr, "Unhandled option: %s\n", opteq+1);
777 } else if(!strcmp(opts, "guest")) {
780 } else if(!strcmp(opts, "rw")) {
782 } else if(!strcmp(opts, "ro")) {
785 strncpy(p, opts, sizeof(pstring) - (p - options) - 1);
799 *(p-1) = 0; /* remove trailing , */
800 DEBUG(3,("passthrough options '%s'\n", options));
804 /****************************************************************************
806 ****************************************************************************/
807 int main(int argc,char *argv[])
815 /* here we are interactive, even if run from autofs */
816 setup_logging("mount.smbfs",True);
818 #if 0 /* JRA - Urban says not needed ? */
819 /* CLI_FORCE_ASCII=false makes smbmount negotiate unicode. The default
820 is to not announce any unicode capabilities as current smbfs does
822 p = getenv("CLI_FORCE_ASCII");
823 if (p && !strcmp(p, "false"))
824 unsetenv("CLI_FORCE_ASCII");
826 setenv("CLI_FORCE_ASCII", "true", 1);
829 in_client = True; /* Make sure that we tell lp_load we are */
831 if (getenv("USER")) {
832 pstrcpy(username,getenv("USER"));
834 if ((p=strchr_m(username,'%'))) {
836 pstrcpy(password,p+1);
838 memset(strchr_m(getenv("USER"),'%')+1,'X',strlen(password));
843 if (getenv("PASSWD")) {
844 pstrcpy(password,getenv("PASSWD"));
848 if (getenv("PASSWD_FD") || getenv("PASSWD_FILE")) {
853 if (*username == 0 && getenv("LOGNAME")) {
854 pstrcpy(username,getenv("LOGNAME"));
857 if (!lp_load(dyn_CONFIGFILE,True,False,False)) {
858 fprintf(stderr, "Can't load %s - run testparm to debug it\n",
862 parse_mount_smb(argc, argv);
864 if (*credentials != 0) {
865 read_credentials_file(credentials);
868 DEBUG(3,("mount.smbfs started (version %s)\n", VERSION));
870 if (*workgroup == 0) {
871 pstrcpy(workgroup,lp_workgroup());
875 if (!*my_netbios_name) {
876 pstrcpy(my_netbios_name, myhostname());
878 strupper(my_netbios_name);