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
) {
81 /* If we get here - the child exited with some error status */
85 signal( SIGTERM
, SIG_DFL
);
89 static void close_our_files(int client_fd
)
94 getrlimit(RLIMIT_NOFILE
,&limits
);
95 for (i
= 0; i
< limits
.rlim_max
; i
++) {
102 static void usr1_handler(int x
)
108 /*****************************************************
109 return a connection to a server
110 *******************************************************/
111 static struct cli_state
*do_connection(char *the_service
)
114 struct nmb_name called
, calling
;
120 if (the_service
[0] != '\\' || the_service
[1] != '\\') {
125 pstrcpy(server
, the_service
+2);
126 share
= strchr_m(server
,'\\');
136 make_nmb_name(&calling
, my_netbios_name
, 0x0);
137 make_nmb_name(&called
, server
, 0x20);
141 if (have_ip
) ip
= dest_ip
;
143 /* have to open a new connection */
144 if (!(c
=cli_initialise(NULL
)) || (cli_set_port(c
, smb_port
) != smb_port
) ||
145 !cli_connect(c
, server_n
, &ip
)) {
146 DEBUG(0,("%d: Connection to %s failed\n", sys_getpid(), server_n
));
153 /* SPNEGO doesn't work till we get NTSTATUS error support */
154 c
->use_spnego
= False
;
156 if (!cli_session_request(c
, &calling
, &called
)) {
158 DEBUG(0,("%d: session request to %s failed (%s)\n",
159 sys_getpid(), called
.name
, cli_errstr(c
)));
161 if ((p
=strchr_m(called
.name
, '.'))) {
165 if (strcmp(called
.name
, "*SMBSERVER")) {
166 make_nmb_name(&called
, "*SMBSERVER", 0x20);
172 DEBUG(4,("%d: session request ok\n", sys_getpid()));
174 if (!cli_negprot(c
)) {
175 DEBUG(0,("%d: protocol negotiation failed\n", sys_getpid()));
181 char *pass
= getpass("Password: ");
183 pstrcpy(password
, pass
);
187 /* This should be right for current smbfs. Future versions will support
188 large files as well as unicode and oplocks. */
189 c
->capabilities
&= ~(CAP_UNICODE
| CAP_LARGE_FILES
| CAP_NT_SMBS
|
190 CAP_NT_FIND
| CAP_STATUS32
| CAP_LEVEL_II_OPLOCKS
);
191 c
->force_dos_errors
= True
;
192 if (!cli_session_setup(c
, username
,
193 password
, strlen(password
),
194 password
, strlen(password
),
196 /* if a password was not supplied then try again with a
198 if (password
[0] || !username
[0] ||
199 !cli_session_setup(c
, "", "", 0, "", 0, workgroup
)) {
200 DEBUG(0,("%d: session setup failed: %s\n",
201 sys_getpid(), cli_errstr(c
)));
205 DEBUG(0,("Anonymous login successful\n"));
208 DEBUG(4,("%d: session setup ok\n", sys_getpid()));
210 if (!cli_send_tconX(c
, share
, "?????",
211 password
, strlen(password
)+1)) {
212 DEBUG(0,("%d: tree connect failed: %s\n",
213 sys_getpid(), cli_errstr(c
)));
218 DEBUG(4,("%d: tconx ok\n", sys_getpid()));
226 /****************************************************************************
227 unmount smbfs (this is a bailout routine to clean up if a reconnect fails)
228 Code blatently stolen from smbumount.c
230 ****************************************************************************/
231 static void smb_umount(char *mount_point
)
239 This routine only gets called to the scene of a disaster
240 to shoot the survivors... A connection that was working
241 has now apparently failed. We have an active mount point
242 (presumably) that we need to dump. If we get errors along
243 the way - make some noise, but we are already turning out
244 the lights to exit anyways...
246 if (umount(mount_point
) != 0) {
247 DEBUG(0,("%d: Could not umount %s: %s\n",
248 sys_getpid(), mount_point
, strerror(errno
)));
252 if ((fd
= open(MOUNTED
"~", O_RDWR
|O_CREAT
|O_EXCL
, 0600)) == -1) {
253 DEBUG(0,("%d: Can't get "MOUNTED
"~ lock file", sys_getpid()));
259 if ((mtab
= setmntent(MOUNTED
, "r")) == NULL
) {
260 DEBUG(0,("%d: Can't open " MOUNTED
": %s\n",
261 sys_getpid(), strerror(errno
)));
265 #define MOUNTED_TMP MOUNTED".tmp"
267 if ((new_mtab
= setmntent(MOUNTED_TMP
, "w")) == NULL
) {
268 DEBUG(0,("%d: Can't open " MOUNTED_TMP
": %s\n",
269 sys_getpid(), strerror(errno
)));
274 while ((mnt
= getmntent(mtab
)) != NULL
) {
275 if (strcmp(mnt
->mnt_dir
, mount_point
) != 0) {
276 addmntent(new_mtab
, mnt
);
282 if (fchmod (fileno (new_mtab
), S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IROTH
) < 0) {
283 DEBUG(0,("%d: Error changing mode of %s: %s\n",
284 sys_getpid(), MOUNTED_TMP
, strerror(errno
)));
290 if (rename(MOUNTED_TMP
, MOUNTED
) < 0) {
291 DEBUG(0,("%d: Cannot rename %s to %s: %s\n",
292 sys_getpid(), MOUNTED
, MOUNTED_TMP
, strerror(errno
)));
296 if (unlink(MOUNTED
"~") == -1) {
297 DEBUG(0,("%d: Can't remove "MOUNTED
"~", sys_getpid()));
304 * Call the smbfs ioctl to install a connection socket,
305 * then wait for a signal to reconnect. Note that we do
306 * not exit after open_sockets() or send_login() errors,
307 * as the smbfs mount would then have no way to recover.
309 static void send_fs_socket(char *the_service
, char *mount_point
, struct cli_state
*c
)
311 int fd
, closed
= 0, res
= 1;
312 pid_t parentpid
= getppid();
313 struct smb_conn_opt conn_options
;
315 memset(&conn_options
, 0, sizeof(conn_options
));
318 if ((fd
= open(mount_point
, O_RDONLY
)) < 0) {
319 DEBUG(0,("mount.smbfs[%d]: can't open %s\n",
320 sys_getpid(), mount_point
));
324 conn_options
.fd
= c
->fd
;
325 conn_options
.protocol
= c
->protocol
;
326 conn_options
.case_handling
= SMB_CASE_DEFAULT
;
327 conn_options
.max_xmit
= c
->max_xmit
;
328 conn_options
.server_uid
= c
->vuid
;
329 conn_options
.tid
= c
->cnum
;
330 conn_options
.secmode
= c
->sec_mode
;
331 conn_options
.rawmode
= 0;
332 conn_options
.sesskey
= c
->sesskey
;
333 conn_options
.maxraw
= 0;
334 conn_options
.capabilities
= c
->capabilities
;
335 conn_options
.serverzone
= c
->serverzone
/60;
337 res
= ioctl(fd
, SMB_IOC_NEWCONN
, &conn_options
);
339 DEBUG(0,("mount.smbfs[%d]: ioctl failed, res=%d\n",
346 /* Ok... We are going to kill the parent. Now
347 is the time to break the process group... */
349 /* Send a signal to the parent to terminate */
350 kill(parentpid
, SIGTERM
);
356 /* This looks wierd but we are only closing the userspace
357 side, the connection has already been passed to smbfs and
358 it has increased the usage count on the socket.
360 If we don't do this we will "leak" sockets and memory on
361 each reconnection we have to make. */
366 /* redirect stdout & stderr since we can't know that
367 the library functions we use are using DEBUG. */
368 if ( (fd
= open("/dev/null", O_WRONLY
)) < 0)
369 DEBUG(2,("mount.smbfs: can't open /dev/null\n"));
372 dup2(fd
, STDOUT_FILENO
);
373 dup2(fd
, STDERR_FILENO
);
377 /* here we are no longer interactive */
378 set_remote_machine_name("smbmount"); /* sneaky ... */
379 setup_logging("mount.smbfs", False
);
381 DEBUG(0, ("mount.smbfs: entering daemon mode for service %s, pid=%d\n", the_service
, sys_getpid()));
386 /* Wait for a signal from smbfs ... but don't continue
387 until we actually get a new connection. */
389 CatchSignal(SIGUSR1
, &usr1_handler
);
391 DEBUG(2,("mount.smbfs[%d]: got signal, getting new socket\n", sys_getpid()));
392 c
= do_connection(the_service
);
396 smb_umount(mount_point
);
397 DEBUG(2,("mount.smbfs[%d]: exit\n", sys_getpid()));
405 static void init_mount(void)
407 char mount_point
[MAXPATHLEN
+1];
414 if (realpath(mpoint
, mount_point
) == NULL
) {
415 fprintf(stderr
, "Could not resolve mount point %s\n", mpoint
);
420 c
= do_connection(service
);
422 fprintf(stderr
,"SMB connection failed\n");
427 Set up to return as a daemon child and wait in the parent
428 until the child say it's ready...
432 pstrcpy(svc2
, service
);
433 string_replace(svc2
, '\\','/');
434 string_replace(svc2
, ' ','_');
436 memset(args
, 0, sizeof(args
[0])*20);
439 args
[i
++] = "smbmnt";
441 args
[i
++] = mount_point
;
449 slprintf(tmp
, sizeof(tmp
)-1, "%d", mount_uid
);
451 args
[i
++] = smb_xstrdup(tmp
);
454 slprintf(tmp
, sizeof(tmp
)-1, "%d", mount_gid
);
456 args
[i
++] = smb_xstrdup(tmp
);
459 slprintf(tmp
, sizeof(tmp
)-1, "0%o", mount_fmask
);
461 args
[i
++] = smb_xstrdup(tmp
);
464 slprintf(tmp
, sizeof(tmp
)-1, "0%o", mount_dmask
);
466 args
[i
++] = smb_xstrdup(tmp
);
473 if (sys_fork() == 0) {
476 asprintf(&smbmnt_path
, "%s/smbmnt", dyn_BINDIR
);
478 if (file_exist(smbmnt_path
, NULL
)) {
479 execv(smbmnt_path
, args
);
481 "smbfs/init_mount: execv of %s failed. Error was %s.",
482 smbmnt_path
, strerror(errno
));
484 execvp("smbmnt", args
);
486 "smbfs/init_mount: execv of %s failed. Error was %s.",
487 "smbmnt", strerror(errno
));
493 if (waitpid(-1, &status
, 0) == -1) {
494 fprintf(stderr
,"waitpid failed: Error was %s", strerror(errno
) );
495 /* FIXME: do some proper error handling */
499 if (WIFEXITED(status
) && WEXITSTATUS(status
) != 0) {
500 fprintf(stderr
,"smbmnt failed: %d\n", WEXITSTATUS(status
));
501 /* FIXME: do some proper error handling */
505 /* Ok... This is the rubicon for that mount point... At any point
506 after this, if the connections fail and can not be reconstructed
507 for any reason, we will have to unmount the mount point. There
508 is no exit from the next call...
510 send_fs_socket(service
, mount_point
, c
);
514 /****************************************************************************
515 get a password from a a file or file descriptor
516 exit on failure (from smbclient, move to libsmb or shared .c file?)
517 ****************************************************************************/
518 static void get_password_file(void)
522 BOOL close_it
= False
;
526 if ((p
= getenv("PASSWD_FD")) != NULL
) {
527 pstrcpy(spec
, "descriptor ");
529 sscanf(p
, "%d", &fd
);
531 } else if ((p
= getenv("PASSWD_FILE")) != NULL
) {
532 fd
= sys_open(p
, O_RDONLY
, 0);
535 fprintf(stderr
, "Error opening PASSWD_FILE %s: %s\n",
536 spec
, strerror(errno
));
542 for(p
= pass
, *p
= '\0'; /* ensure that pass is null-terminated */
543 p
&& p
- pass
< sizeof(pass
);) {
544 switch (read(fd
, p
, 1)) {
546 if (*p
!= '\n' && *p
!= '\0') {
547 *++p
= '\0'; /* advance p, and null-terminate pass */
552 *p
= '\0'; /* null-terminate it, just in case... */
553 p
= NULL
; /* then force the loop condition to become false */
556 fprintf(stderr
, "Error reading password from file %s: %s\n",
557 spec
, "empty password\n");
562 fprintf(stderr
, "Error reading password from file %s: %s\n",
563 spec
, strerror(errno
));
567 pstrcpy(password
, pass
);
572 /****************************************************************************
573 get username and password from a credentials file
574 exit on failure (from smbclient, move to libsmb or shared .c file?)
575 ****************************************************************************/
576 static void read_credentials_file(char *filename
)
581 char *ptr
, *val
, *param
;
583 if ((auth
=sys_fopen(filename
, "r")) == NULL
)
585 /* fail if we can't open the credentials file */
586 DEBUG(0,("ERROR: Unable to open credentials file!\n"));
592 /* get a line from the file */
593 if (!fgets (buf
, sizeof(buf
), auth
))
597 if ((len
) && (buf
[len
-1]=='\n'))
605 /* break up the line into parameter & value.
606 will need to eat a little whitespace possibly */
608 if (!(ptr
= strchr (buf
, '=')))
613 /* eat leading white space */
614 while ((*val
!='\0') && ((*val
==' ') || (*val
=='\t')))
617 if (strwicmp("password", param
) == 0)
619 pstrcpy(password
, val
);
622 else if (strwicmp("username", param
) == 0)
623 pstrcpy(username
, val
);
625 memset(buf
, 0, sizeof(buf
));
631 /****************************************************************************
633 ****************************************************************************/
634 static void usage(void)
636 printf("Usage: mount.smbfs service mountpoint [-o options,...]\n");
638 printf("Version %s\n\n",VERSION
);
642 username=<arg> SMB username\n\
643 password=<arg> SMB password\n\
644 credentials=<filename> file with username/password\n\
645 netbiosname=<arg> source NetBIOS name\n\
646 uid=<arg> mount uid or username\n\
647 gid=<arg> mount gid or groupname\n\
648 port=<arg> remote SMB port number\n\
649 fmask=<arg> file umask\n\
650 dmask=<arg> directory umask\n\
651 debug=<arg> debug level\n\
652 ip=<arg> destination host or IP address\n\
653 workgroup=<arg> workgroup on destination\n\
654 sockopt=<arg> TCP socket options\n\
655 scope=<arg> NetBIOS scope\n\
656 iocharset=<arg> Linux charset (iso8859-1, utf8)\n\
657 codepage=<arg> server codepage (cp850)\n\
658 ttl=<arg> dircache time to live\n\
659 guest don't prompt for a password\n\
660 ro mount read-only\n\
661 rw mount read-write\n\
663 This command is designed to be run from within /bin/mount by giving\n\
664 the option '-t smbfs'. For example:\n\
665 mount -t smbfs -o username=tridge,password=foobar //fjall/test /data/test\n\
670 /****************************************************************************
671 Argument parsing for mount.smbfs interface
672 mount will call us like this:
673 mount.smbfs device mountpoint -o <options>
675 <options> is never empty, containing at least rw or ro
676 ****************************************************************************/
677 static void parse_mount_smb(int argc
, char **argv
)
686 if (argc
< 2 || argv
[1][0] == '-') {
691 pstrcpy(service
, argv
[1]);
692 pstrcpy(mpoint
, argv
[2]);
694 /* Convert any '/' characters in the service name to
696 string_replace(service
, '/','\\');
700 opt
= getopt(argc
, argv
, "o:");
709 * option parsing from nfsmount.c (util-linux-2.9u)
711 for (opts
= strtok(optarg
, ","); opts
; opts
= strtok(NULL
, ",")) {
712 DEBUG(3, ("opts: %s\n", opts
));
713 if ((opteq
= strchr_m(opts
, '='))) {
714 val
= atoi(opteq
+ 1);
717 if (!strcmp(opts
, "username") ||
718 !strcmp(opts
, "logon")) {
720 pstrcpy(username
,opteq
+1);
721 if ((lp
=strchr_m(username
,'%'))) {
723 pstrcpy(password
,lp
+1);
725 memset(strchr_m(opteq
+1,'%')+1,'X',strlen(password
));
727 if ((lp
=strchr_m(username
,'/'))) {
729 pstrcpy(workgroup
,lp
+1);
731 } else if(!strcmp(opts
, "passwd") ||
732 !strcmp(opts
, "password")) {
733 pstrcpy(password
,opteq
+1);
735 memset(opteq
+1,'X',strlen(password
));
736 } else if(!strcmp(opts
, "credentials")) {
737 pstrcpy(credentials
,opteq
+1);
738 } else if(!strcmp(opts
, "netbiosname")) {
739 pstrcpy(my_netbios_name
,opteq
+1);
740 } else if(!strcmp(opts
, "uid")) {
741 mount_uid
= nametouid(opteq
+1);
742 } else if(!strcmp(opts
, "gid")) {
743 mount_gid
= nametogid(opteq
+1);
744 } else if(!strcmp(opts
, "port")) {
746 } else if(!strcmp(opts
, "fmask")) {
747 mount_fmask
= strtol(opteq
+1, NULL
, 8);
748 } else if(!strcmp(opts
, "dmask")) {
749 mount_dmask
= strtol(opteq
+1, NULL
, 8);
750 } else if(!strcmp(opts
, "debug")) {
752 } else if(!strcmp(opts
, "ip")) {
753 dest_ip
= *interpret_addr2(opteq
+1);
754 if (is_zero_ip(dest_ip
)) {
755 fprintf(stderr
,"Can't resolve address %s\n", opteq
+1);
759 } else if(!strcmp(opts
, "workgroup")) {
760 pstrcpy(workgroup
,opteq
+1);
761 } else if(!strcmp(opts
, "sockopt")) {
762 pstrcpy(user_socket_options
,opteq
+1);
763 } else if(!strcmp(opts
, "scope")) {
764 pstrcpy(global_scope(),opteq
+1);
766 slprintf(p
, sizeof(pstring
) - (p
- options
) - 1, "%s=%s,", opts
, opteq
+1);
771 if(!strcmp(opts
, "nocaps")) {
772 fprintf(stderr
, "Unhandled option: %s\n", opteq
+1);
774 } else if(!strcmp(opts
, "guest")) {
777 } else if(!strcmp(opts
, "rw")) {
779 } else if(!strcmp(opts
, "ro")) {
782 strncpy(p
, opts
, sizeof(pstring
) - (p
- options
) - 1);
796 *(p
-1) = 0; /* remove trailing , */
797 DEBUG(3,("passthrough options '%s'\n", options
));
801 /****************************************************************************
803 ****************************************************************************/
804 int main(int argc
,char *argv
[])
812 /* here we are interactive, even if run from autofs */
813 setup_logging("mount.smbfs",True
);
815 #if 0 /* JRA - Urban says not needed ? */
816 /* CLI_FORCE_ASCII=false makes smbmount negotiate unicode. The default
817 is to not announce any unicode capabilities as current smbfs does
819 p
= getenv("CLI_FORCE_ASCII");
820 if (p
&& !strcmp(p
, "false"))
821 unsetenv("CLI_FORCE_ASCII");
823 setenv("CLI_FORCE_ASCII", "true", 1);
826 in_client
= True
; /* Make sure that we tell lp_load we are */
828 if (getenv("USER")) {
829 pstrcpy(username
,getenv("USER"));
831 if ((p
=strchr_m(username
,'%'))) {
833 pstrcpy(password
,p
+1);
835 memset(strchr_m(getenv("USER"),'%')+1,'X',strlen(password
));
840 if (getenv("PASSWD")) {
841 pstrcpy(password
,getenv("PASSWD"));
845 if (getenv("PASSWD_FD") || getenv("PASSWD_FILE")) {
850 if (*username
== 0 && getenv("LOGNAME")) {
851 pstrcpy(username
,getenv("LOGNAME"));
854 if (!lp_load(dyn_CONFIGFILE
,True
,False
,False
)) {
855 fprintf(stderr
, "Can't load %s - run testparm to debug it\n",
859 parse_mount_smb(argc
, argv
);
861 if (*credentials
!= 0) {
862 read_credentials_file(credentials
);
865 DEBUG(3,("mount.smbfs started (version %s)\n", VERSION
));
867 if (*workgroup
== 0) {
868 pstrcpy(workgroup
,lp_workgroup());
872 if (!*my_netbios_name
) {
873 pstrcpy(my_netbios_name
, myhostname());
875 strupper(my_netbios_name
);