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 */
510 } else if (WIFSIGNALED(status
)) {
511 fprintf(stderr
, "smbmnt killed by signal %d\n", WTERMSIG(status
));
515 /* Ok... This is the rubicon for that mount point... At any point
516 after this, if the connections fail and can not be reconstructed
517 for any reason, we will have to unmount the mount point. There
518 is no exit from the next call...
520 send_fs_socket(service
, mount_point
, c
);
524 /****************************************************************************
525 get a password from a a file or file descriptor
526 exit on failure (from smbclient, move to libsmb or shared .c file?)
527 ****************************************************************************/
528 static void get_password_file(void)
532 BOOL close_it
= False
;
536 if ((p
= getenv("PASSWD_FD")) != NULL
) {
537 pstrcpy(spec
, "descriptor ");
539 sscanf(p
, "%d", &fd
);
541 } else if ((p
= getenv("PASSWD_FILE")) != NULL
) {
542 fd
= sys_open(p
, O_RDONLY
, 0);
545 fprintf(stderr
, "Error opening PASSWD_FILE %s: %s\n",
546 spec
, strerror(errno
));
552 for(p
= pass
, *p
= '\0'; /* ensure that pass is null-terminated */
553 p
&& p
- pass
< sizeof(pass
);) {
554 switch (read(fd
, p
, 1)) {
556 if (*p
!= '\n' && *p
!= '\0') {
557 *++p
= '\0'; /* advance p, and null-terminate pass */
562 *p
= '\0'; /* null-terminate it, just in case... */
563 p
= NULL
; /* then force the loop condition to become false */
566 fprintf(stderr
, "Error reading password from file %s: %s\n",
567 spec
, "empty password\n");
572 fprintf(stderr
, "Error reading password from file %s: %s\n",
573 spec
, strerror(errno
));
577 pstrcpy(password
, pass
);
582 /****************************************************************************
583 get username and password from a credentials file
584 exit on failure (from smbclient, move to libsmb or shared .c file?)
585 ****************************************************************************/
586 static void read_credentials_file(char *filename
)
591 char *ptr
, *val
, *param
;
593 if ((auth
=sys_fopen(filename
, "r")) == NULL
)
595 /* fail if we can't open the credentials file */
596 DEBUG(0,("ERROR: Unable to open credentials file!\n"));
602 /* get a line from the file */
603 if (!fgets (buf
, sizeof(buf
), auth
))
607 if ((len
) && (buf
[len
-1]=='\n'))
615 /* break up the line into parameter & value.
616 will need to eat a little whitespace possibly */
618 if (!(ptr
= strchr (buf
, '=')))
623 /* eat leading white space */
624 while ((*val
!='\0') && ((*val
==' ') || (*val
=='\t')))
627 if (strwicmp("password", param
) == 0)
629 pstrcpy(password
, val
);
632 else if (strwicmp("username", param
) == 0)
633 pstrcpy(username
, val
);
635 memset(buf
, 0, sizeof(buf
));
641 /****************************************************************************
643 ****************************************************************************/
644 static void usage(void)
646 printf("Usage: mount.smbfs service mountpoint [-o options,...]\n");
648 printf("Version %s\n\n",VERSION
);
652 username=<arg> SMB username\n\
653 password=<arg> SMB password\n\
654 credentials=<filename> file with username/password\n\
655 netbiosname=<arg> source NetBIOS name\n\
656 uid=<arg> mount uid or username\n\
657 gid=<arg> mount gid or groupname\n\
658 port=<arg> remote SMB port number\n\
659 fmask=<arg> file umask\n\
660 dmask=<arg> directory umask\n\
661 debug=<arg> debug level\n\
662 ip=<arg> destination host or IP address\n\
663 workgroup=<arg> workgroup on destination\n\
664 sockopt=<arg> TCP socket options\n\
665 scope=<arg> NetBIOS scope\n\
666 iocharset=<arg> Linux charset (iso8859-1, utf8)\n\
667 codepage=<arg> server codepage (cp850)\n\
668 ttl=<arg> dircache time to live\n\
669 guest don't prompt for a password\n\
670 ro mount read-only\n\
671 rw mount read-write\n\
673 This command is designed to be run from within /bin/mount by giving\n\
674 the option '-t smbfs'. For example:\n\
675 mount -t smbfs -o username=tridge,password=foobar //fjall/test /data/test\n\
680 /****************************************************************************
681 Argument parsing for mount.smbfs interface
682 mount will call us like this:
683 mount.smbfs device mountpoint -o <options>
685 <options> is never empty, containing at least rw or ro
686 ****************************************************************************/
687 static void parse_mount_smb(int argc
, char **argv
)
696 /* FIXME: This function can silently fail if the arguments are
697 * not in the expected order.
699 > The arguments syntax of smbmount 2.2.3a (smbfs of Debian stable)
700 > requires that one gives "-o" before further options like username=...
701 > . Without -o, the username=.. setting is *silently* ignored. I've
702 > spent about an hour trying to find out why I couldn't log in now..
707 if (argc
< 2 || argv
[1][0] == '-') {
712 pstrcpy(service
, argv
[1]);
713 pstrcpy(mpoint
, argv
[2]);
715 /* Convert any '/' characters in the service name to
717 string_replace(service
, '/','\\');
721 opt
= getopt(argc
, argv
, "o:");
730 * option parsing from nfsmount.c (util-linux-2.9u)
732 for (opts
= strtok(optarg
, ","); opts
; opts
= strtok(NULL
, ",")) {
733 DEBUG(3, ("opts: %s\n", opts
));
734 if ((opteq
= strchr_m(opts
, '='))) {
735 val
= atoi(opteq
+ 1);
738 if (!strcmp(opts
, "username") ||
739 !strcmp(opts
, "logon")) {
741 pstrcpy(username
,opteq
+1);
742 if ((lp
=strchr_m(username
,'%'))) {
744 pstrcpy(password
,lp
+1);
746 memset(strchr_m(opteq
+1,'%')+1,'X',strlen(password
));
748 if ((lp
=strchr_m(username
,'/'))) {
750 pstrcpy(workgroup
,lp
+1);
752 } else if(!strcmp(opts
, "passwd") ||
753 !strcmp(opts
, "password")) {
754 pstrcpy(password
,opteq
+1);
756 memset(opteq
+1,'X',strlen(password
));
757 } else if(!strcmp(opts
, "credentials")) {
758 pstrcpy(credentials
,opteq
+1);
759 } else if(!strcmp(opts
, "netbiosname")) {
760 pstrcpy(my_netbios_name
,opteq
+1);
761 } else if(!strcmp(opts
, "uid")) {
762 mount_uid
= nametouid(opteq
+1);
763 } else if(!strcmp(opts
, "gid")) {
764 mount_gid
= nametogid(opteq
+1);
765 } else if(!strcmp(opts
, "port")) {
767 } else if(!strcmp(opts
, "fmask")) {
768 mount_fmask
= strtol(opteq
+1, NULL
, 8);
769 } else if(!strcmp(opts
, "dmask")) {
770 mount_dmask
= strtol(opteq
+1, NULL
, 8);
771 } else if(!strcmp(opts
, "debug")) {
773 } else if(!strcmp(opts
, "ip")) {
774 dest_ip
= *interpret_addr2(opteq
+1);
775 if (is_zero_ip(dest_ip
)) {
776 fprintf(stderr
,"Can't resolve address %s\n", opteq
+1);
780 } else if(!strcmp(opts
, "workgroup")) {
781 pstrcpy(workgroup
,opteq
+1);
782 } else if(!strcmp(opts
, "sockopt")) {
783 pstrcpy(user_socket_options
,opteq
+1);
784 } else if(!strcmp(opts
, "scope")) {
785 set_global_scope(opteq
+1);
787 slprintf(p
, sizeof(pstring
) - (p
- options
) - 1, "%s=%s,", opts
, opteq
+1);
792 if(!strcmp(opts
, "nocaps")) {
793 fprintf(stderr
, "Unhandled option: %s\n", opteq
+1);
795 } else if(!strcmp(opts
, "guest")) {
798 } else if(!strcmp(opts
, "rw")) {
800 } else if(!strcmp(opts
, "ro")) {
803 strncpy(p
, opts
, sizeof(pstring
) - (p
- options
) - 1);
817 *(p
-1) = 0; /* remove trailing , */
818 DEBUG(3,("passthrough options '%s'\n", options
));
822 /****************************************************************************
824 ****************************************************************************/
825 int main(int argc
,char *argv
[])
833 /* here we are interactive, even if run from autofs */
834 setup_logging("mount.smbfs",True
);
836 #if 0 /* JRA - Urban says not needed ? */
837 /* CLI_FORCE_ASCII=false makes smbmount negotiate unicode. The default
838 is to not announce any unicode capabilities as current smbfs does
840 p
= getenv("CLI_FORCE_ASCII");
841 if (p
&& !strcmp(p
, "false"))
842 unsetenv("CLI_FORCE_ASCII");
844 setenv("CLI_FORCE_ASCII", "true", 1);
847 in_client
= True
; /* Make sure that we tell lp_load we are */
849 if (getenv("USER")) {
850 pstrcpy(username
,getenv("USER"));
852 if ((p
=strchr_m(username
,'%'))) {
854 pstrcpy(password
,p
+1);
856 memset(strchr_m(getenv("USER"),'%')+1,'X',strlen(password
));
861 if (getenv("PASSWD")) {
862 pstrcpy(password
,getenv("PASSWD"));
866 if (getenv("PASSWD_FD") || getenv("PASSWD_FILE")) {
871 if (*username
== 0 && getenv("LOGNAME")) {
872 pstrcpy(username
,getenv("LOGNAME"));
875 if (!lp_load(dyn_CONFIGFILE
,True
,False
,False
)) {
876 fprintf(stderr
, "Can't load %s - run testparm to debug it\n",
880 parse_mount_smb(argc
, argv
);
882 if (*credentials
!= 0) {
883 read_credentials_file(credentials
);
886 DEBUG(3,("mount.smbfs started (version %s)\n", VERSION
));
888 if (*workgroup
== 0) {
889 pstrcpy(workgroup
,lp_workgroup());
893 if (!*my_netbios_name
) {
894 pstrcpy(my_netbios_name
, myhostname());
896 strupper(my_netbios_name
);