2 Unix SMB/Netbios implementation.
5 Copyright (C) Andrew Tridgell 1999
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27 #include <asm/types.h>
28 #include <linux/smb_fs.h>
30 extern struct in_addr ipzero
;
31 extern int DEBUGLEVEL
;
32 extern BOOL in_client
;
33 extern pstring user_socket_options
;
34 extern BOOL append_log
;
35 extern fstring remote_machine
;
37 static pstring credentials
;
38 static pstring my_netbios_name
;
39 static pstring password
;
40 static pstring username
;
41 static pstring workgroup
;
42 static pstring mpoint
;
43 static pstring service
;
44 static pstring options
;
46 static struct in_addr dest_ip
;
48 static int smb_port
= 139;
50 static uid_t mount_uid
;
51 static gid_t mount_gid
;
53 static unsigned mount_fmask
;
54 static unsigned mount_dmask
;
56 static void usage(void);
58 static void exit_parent(int sig
)
60 /* parent simply exits when child says go... */
64 static void daemonize(void)
69 signal( SIGTERM
, exit_parent
);
71 if ((child_pid
= fork()) < 0) {
72 DEBUG(0,("could not fork\n"));
77 j
= waitpid( child_pid
, &status
, 0 );
79 if( EINTR
== errno
) {
86 /* If we get here - the child exited with some error status */
90 signal( SIGTERM
, SIG_DFL
);
94 static void close_our_files(int client_fd
)
99 getrlimit(RLIMIT_NOFILE
,&limits
);
100 for (i
= 0; i
< limits
.rlim_max
; i
++) {
107 static void usr1_handler(int x
)
113 /*****************************************************
114 return a connection to a server
115 *******************************************************/
117 static struct cli_state
*do_connection(char *svc_name
)
120 struct nmb_name called
, calling
;
126 if (svc_name
[0] != '\\' || svc_name
[1] != '\\') {
131 pstrcpy(server
, svc_name
+2);
132 share
= strchr(server
,'\\');
144 make_nmb_name(&calling
, my_netbios_name
, 0x0);
145 make_nmb_name(&called
, server
, 0x20);
149 if (have_ip
) ip
= dest_ip
;
151 /* have to open a new connection */
152 if (!(c
=cli_initialise(NULL
)) || (cli_set_port(c
, smb_port
) == 0) ||
153 !cli_connect(c
, server_n
, &ip
)) {
154 DEBUG(0,("%d: Connection to %s failed\n", getpid(), server_n
));
161 if (!cli_session_request(c
, &calling
, &called
)) {
163 DEBUG(0,("%d: session request to %s failed (%s)\n",
164 getpid(), called
.name
, cli_errstr(c
)));
166 if ((p
=strchr(called
.name
, '.'))) {
170 if (strcmp(called
.name
, "*SMBSERVER")) {
171 make_nmb_name(&called
, "*SMBSERVER", 0x20);
177 DEBUG(4,("%d: session request ok\n", getpid()));
179 if (!cli_negprot(c
)) {
180 DEBUG(0,("%d: protocol negotiation failed\n", getpid()));
186 char *pass
= getpass("Password: ");
188 pstrcpy(password
, pass
);
192 /* This should be right for current smbfs. Future versions will support
193 large files as well as unicode and oplocks. */
194 c
->capabilities
&= ~(CAP_UNICODE
| CAP_LARGE_FILES
| CAP_NT_SMBS
|
195 CAP_NT_FIND
| CAP_STATUS32
| CAP_LEVEL_II_OPLOCKS
);
196 if (!cli_session_setup(c
, username
,
197 password
, strlen(password
),
198 password
, strlen(password
),
200 /* if a password was not supplied then try again with a
202 if (password
[0] || !username
[0] ||
203 !cli_session_setup(c
, "", "", 0, "", 0, workgroup
)) {
204 DEBUG(0,("%d: session setup failed: %s\n",
205 getpid(), cli_errstr(c
)));
209 DEBUG(0,("Anonymous login successful\n"));
212 DEBUG(4,("%d: session setup ok\n", getpid()));
214 if (!cli_send_tconX(c
, share
, "?????",
215 password
, strlen(password
)+1)) {
216 DEBUG(0,("%d: tree connect failed: %s\n",
217 getpid(), cli_errstr(c
)));
222 DEBUG(4,("%d: tconx ok\n", getpid()));
230 /****************************************************************************
231 unmount smbfs (this is a bailout routine to clean up if a reconnect fails)
232 Code blatently stolen from smbumount.c
234 ****************************************************************************/
235 static void smb_umount(char *mount_point
)
243 This routine only gets called to the scene of a disaster
244 to shoot the survivors... A connection that was working
245 has now apparently failed. We have an active mount point
246 (presumably) that we need to dump. If we get errors along
247 the way - make some noise, but we are already turning out
248 the lights to exit anyways...
250 if (umount(mount_point
) != 0) {
251 DEBUG(0,("%d: Could not umount %s: %s\n",
252 getpid(), mount_point
, strerror(errno
)));
256 if ((fd
= open(MOUNTED
"~", O_RDWR
|O_CREAT
|O_EXCL
, 0600)) == -1) {
257 DEBUG(0,("%d: Can't get "MOUNTED
"~ lock file", getpid()));
263 if ((mtab
= setmntent(MOUNTED
, "r")) == NULL
) {
264 DEBUG(0,("%d: Can't open " MOUNTED
": %s\n",
265 getpid(), strerror(errno
)));
269 #define MOUNTED_TMP MOUNTED".tmp"
271 if ((new_mtab
= setmntent(MOUNTED_TMP
, "w")) == NULL
) {
272 DEBUG(0,("%d: Can't open " MOUNTED_TMP
": %s\n",
273 getpid(), strerror(errno
)));
278 while ((mnt
= getmntent(mtab
)) != NULL
) {
279 if (strcmp(mnt
->mnt_dir
, mount_point
) != 0) {
280 addmntent(new_mtab
, mnt
);
286 if (fchmod (fileno (new_mtab
), S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IROTH
) < 0) {
287 DEBUG(0,("%d: Error changing mode of %s: %s\n",
288 getpid(), MOUNTED_TMP
, strerror(errno
)));
294 if (rename(MOUNTED_TMP
, MOUNTED
) < 0) {
295 DEBUG(0,("%d: Cannot rename %s to %s: %s\n",
296 getpid(), MOUNTED
, MOUNTED_TMP
, strerror(errno
)));
300 if (unlink(MOUNTED
"~") == -1) {
301 DEBUG(0,("%d: Can't remove "MOUNTED
"~", getpid()));
308 * Call the smbfs ioctl to install a connection socket,
309 * then wait for a signal to reconnect. Note that we do
310 * not exit after open_sockets() or send_login() errors,
311 * as the smbfs mount would then have no way to recover.
313 static void send_fs_socket(char *svc_name
, char *mount_point
, struct cli_state
*c
)
315 int fd
, closed
= 0, res
= 1;
316 pid_t parentpid
= getppid();
317 struct smb_conn_opt conn_options
;
319 memset(&conn_options
, 0, sizeof(conn_options
));
322 if ((fd
= open(mount_point
, O_RDONLY
)) < 0) {
323 DEBUG(0,("mount.smbfs[%d]: can't open %s\n",
324 getpid(), mount_point
));
328 conn_options
.fd
= c
->fd
;
329 conn_options
.protocol
= c
->protocol
;
330 conn_options
.case_handling
= SMB_CASE_DEFAULT
;
331 conn_options
.max_xmit
= c
->max_xmit
;
332 conn_options
.server_uid
= c
->vuid
;
333 conn_options
.tid
= c
->cnum
;
334 conn_options
.secmode
= c
->sec_mode
;
335 conn_options
.rawmode
= 0;
336 conn_options
.sesskey
= c
->sesskey
;
337 conn_options
.maxraw
= 0;
338 conn_options
.capabilities
= c
->capabilities
;
339 conn_options
.serverzone
= c
->serverzone
/60;
341 res
= ioctl(fd
, SMB_IOC_NEWCONN
, &conn_options
);
343 DEBUG(0,("mount.smbfs[%d]: ioctl failed, res=%d\n",
350 /* Ok... We are going to kill the parent. Now
351 is the time to break the process group... */
353 /* Send a signal to the parent to terminate */
354 kill(parentpid
, SIGTERM
);
360 /* This looks wierd but we are only closing the userspace
361 side, the connection has already been passed to smbfs and
362 it has increased the usage count on the socket.
364 If we don't do this we will "leak" sockets and memory on
365 each reconnection we have to make. */
370 /* redirect stdout & stderr since we can't know that
371 the library functions we use are using DEBUG. */
372 if ( (fd
= open("/dev/null", O_WRONLY
)) < 0)
373 DEBUG(2,("mount.smbfs: can't open /dev/null\n"));
376 dup2(fd
, STDOUT_FILENO
);
377 dup2(fd
, STDERR_FILENO
);
381 /* here we are no longer interactive */
382 pstrcpy(remote_machine
, "smbmount"); /* sneaky ... */
383 setup_logging("mount.smbfs", False
);
386 DEBUG(0, ("mount.smbfs: entering daemon mode for service %s, pid=%d\n", svc_name
, getpid()));
391 /* Wait for a signal from smbfs ... but don't continue
392 until we actually get a new connection. */
394 CatchSignal(SIGUSR1
, &usr1_handler
);
396 DEBUG(2,("mount.smbfs[%d]: got signal, getting new socket\n", getpid()));
397 c
= do_connection(svc_name
);
401 smb_umount(mount_point
);
402 DEBUG(2,("mount.smbfs[%d]: exit\n", getpid()));
406 /*********************************************************
408 **********************************************************/
409 static char *xstrdup(char *s
)
413 fprintf(stderr
,"out of memory\n");
420 /****************************************************************************
422 ****************************************************************************/
423 static void init_mount(void)
425 char mount_point
[MAXPATHLEN
+1];
432 if (realpath(mpoint
, mount_point
) == NULL
) {
433 fprintf(stderr
, "Could not resolve mount point %s\n", mpoint
);
438 c
= do_connection(service
);
440 fprintf(stderr
,"SMB connection failed\n");
445 Set up to return as a daemon child and wait in the parent
446 until the child say it's ready...
450 pstrcpy(svc2
, service
);
451 string_replace(svc2
, '\\','/');
452 string_replace(svc2
, ' ','_');
454 memset(args
, 0, sizeof(args
[0])*20);
457 args
[i
++] = "smbmnt";
459 args
[i
++] = mount_point
;
467 slprintf(tmp
, sizeof(tmp
)-1, "%d", mount_uid
);
469 args
[i
++] = xstrdup(tmp
);
472 slprintf(tmp
, sizeof(tmp
)-1, "%d", mount_gid
);
474 args
[i
++] = xstrdup(tmp
);
477 slprintf(tmp
, sizeof(tmp
)-1, "0%o", mount_fmask
);
479 args
[i
++] = xstrdup(tmp
);
482 slprintf(tmp
, sizeof(tmp
)-1, "0%o", mount_dmask
);
484 args
[i
++] = xstrdup(tmp
);
492 if (file_exist(BINDIR
"/smbmnt", NULL
)) {
493 execv(BINDIR
"/smbmnt", args
);
494 fprintf(stderr
,"execv of %s failed. Error was %s.", BINDIR
"/smbmnt", strerror(errno
));
496 execvp("smbmnt", args
);
497 fprintf(stderr
,"execvp of smbmnt failed. Error was %s.", strerror(errno
) );
502 if (waitpid(-1, &status
, 0) == -1) {
503 fprintf(stderr
,"waitpid failed: Error was %s", strerror(errno
) );
504 /* FIXME: do some proper error handling */
508 if (WIFEXITED(status
) && WEXITSTATUS(status
) != 0) {
509 fprintf(stderr
,"smbmnt failed: %d\n", WEXITSTATUS(status
));
510 /* FIXME: do some proper error handling */
514 /* Ok... This is the rubicon for that mount point... At any point
515 after this, if the connections fail and can not be reconstructed
516 for any reason, we will have to unmount the mount point. There
517 is no exit from the next call...
519 send_fs_socket(service
, mount_point
, c
);
523 /****************************************************************************
524 get a password from a a file or file descriptor
525 exit on failure (from smbclient, move to libsmb or shared .c file?)
526 ****************************************************************************/
527 static void get_password_file(void)
531 BOOL close_it
= False
;
535 if ((p
= getenv("PASSWD_FD")) != NULL
) {
536 pstrcpy(spec
, "descriptor ");
538 sscanf(p
, "%d", &fd
);
540 } else if ((p
= getenv("PASSWD_FILE")) != NULL
) {
541 fd
= sys_open(p
, O_RDONLY
, 0);
544 fprintf(stderr
, "Error opening PASSWD_FILE %s: %s\n",
545 spec
, strerror(errno
));
551 for(p
= pass
, *p
= '\0'; /* ensure that pass is null-terminated */
552 p
&& p
- pass
< sizeof(pass
);) {
553 switch (read(fd
, p
, 1)) {
555 if (*p
!= '\n' && *p
!= '\0') {
556 *++p
= '\0'; /* advance p, and null-terminate pass */
561 *p
= '\0'; /* null-terminate it, just in case... */
562 p
= NULL
; /* then force the loop condition to become false */
565 fprintf(stderr
, "Error reading password from file %s: %s\n",
566 spec
, "empty password\n");
571 fprintf(stderr
, "Error reading password from file %s: %s\n",
572 spec
, strerror(errno
));
576 pstrcpy(password
, pass
);
581 /****************************************************************************
582 get username and password from a credentials file
583 exit on failure (from smbclient, move to libsmb or shared .c file?)
584 ****************************************************************************/
585 static void read_credentials_file(char *filename
)
590 char *ptr
, *val
, *param
;
592 if ((auth
=sys_fopen(filename
, "r")) == NULL
)
594 /* fail if we can't open the credentials file */
595 DEBUG(0,("ERROR: Unable to open credentials file!\n"));
601 /* get a line from the file */
602 if (!fgets (buf
, sizeof(buf
), auth
))
606 if ((len
) && (buf
[len
-1]=='\n'))
614 /* break up the line into parameter & value.
615 will need to eat a little whitespace possibly */
617 if (!(ptr
= strchr (buf
, '=')))
622 /* eat leading white space */
623 while ((*val
!='\0') && ((*val
==' ') || (*val
=='\t')))
626 if (strstr(param
, "password") == 0)
628 pstrcpy(password
, val
);
631 else if (strstr(param
, "username") == 0)
632 pstrcpy(username
, val
);
634 memset(buf
, 0, sizeof(buf
));
640 /****************************************************************************
642 ****************************************************************************/
643 static void usage(void)
645 printf("Usage: mount.smbfs service mountpoint [-o options,...]\n");
647 printf("Version %s\n\n",VERSION
);
651 username=<arg> SMB username\n\
652 password=<arg> SMB password\n\
653 credentials=<filename> file with username/password\n\
654 netbiosname=<arg> source NetBIOS name\n\
655 uid=<arg> mount uid or username\n\
656 gid=<arg> mount gid or groupname\n\
657 port=<arg> remote SMB port number\n\
658 fmask=<arg> file umask\n\
659 dmask=<arg> directory umask\n\
660 debug=<arg> debug level\n\
661 ip=<arg> destination host or IP address\n\
662 workgroup=<arg> workgroup on destination\n\
663 sockopt=<arg> TCP socket options\n\
664 scope=<arg> NetBIOS scope\n\
665 iocharset=<arg> Linux charset (iso8859-1, utf8)\n\
666 codepage=<arg> server codepage (cp850)\n\
667 ttl=<arg> dircache time to live\n\
668 guest don't prompt for a password\n\
669 ro mount read-only\n\
670 rw mount read-write\n\
672 This command is designed to be run from within /bin/mount by giving\n\
673 the option '-t smbfs'. For example:\n\
674 mount -t smbfs -o username=tridge,password=foobar //fjall/test /data/test\n\
679 /****************************************************************************
680 Argument parsing for mount.smbfs interface
681 mount will call us like this:
682 mount.smbfs device mountpoint -o <options>
684 <options> is never empty, containing at least rw or ro
685 ****************************************************************************/
686 static void parse_mount_smb(int argc
, char **argv
)
693 extern pstring global_scope
;
696 if (argc
< 2 || argv
[1][0] == '-') {
701 pstrcpy(service
, argv
[1]);
702 pstrcpy(mpoint
, argv
[2]);
704 /* Convert any '/' characters in the service name to
706 string_replace(service
, '/','\\');
710 opt
= getopt(argc
, argv
, "o:");
719 * option parsing from nfsmount.c (util-linux-2.9u)
721 for (opts
= strtok(optarg
, ","); opts
; opts
= strtok(NULL
, ",")) {
722 DEBUG(3, ("opts: %s\n", opts
));
723 if ((opteq
= strchr(opts
, '='))) {
724 val
= atoi(opteq
+ 1);
727 if (!strcmp(opts
, "username") ||
728 !strcmp(opts
, "logon")) {
730 pstrcpy(username
,opteq
+1);
731 if ((lp
=strchr(username
,'%'))) {
733 pstrcpy(password
,lp
+1);
735 memset(strchr(opteq
+1,'%')+1,'X',strlen(password
));
737 if ((lp
=strchr(username
,'/'))) {
739 pstrcpy(workgroup
,lp
+1);
741 } else if(!strcmp(opts
, "passwd") ||
742 !strcmp(opts
, "password")) {
743 pstrcpy(password
,opteq
+1);
745 memset(opteq
+1,'X',strlen(password
));
746 } else if(!strcmp(opts
, "credentials")) {
747 pstrcpy(credentials
,opteq
+1);
748 } else if(!strcmp(opts
, "netbiosname")) {
749 pstrcpy(my_netbios_name
,opteq
+1);
750 } else if(!strcmp(opts
, "uid")) {
751 mount_uid
= nametouid(opteq
+1);
752 } else if(!strcmp(opts
, "gid")) {
753 mount_gid
= nametogid(opteq
+1);
754 } else if(!strcmp(opts
, "port")) {
756 } else if(!strcmp(opts
, "fmask")) {
757 mount_fmask
= strtol(opteq
+1, NULL
, 8);
758 } else if(!strcmp(opts
, "dmask")) {
759 mount_dmask
= strtol(opteq
+1, NULL
, 8);
760 } else if(!strcmp(opts
, "debug")) {
762 } else if(!strcmp(opts
, "ip")) {
763 dest_ip
= *interpret_addr2(opteq
+1);
764 if (zero_ip(dest_ip
)) {
765 fprintf(stderr
,"Can't resolve address %s\n", opteq
+1);
769 } else if(!strcmp(opts
, "workgroup")) {
770 pstrcpy(workgroup
,opteq
+1);
771 } else if(!strcmp(opts
, "sockopt")) {
772 pstrcpy(user_socket_options
,opteq
+1);
773 } else if(!strcmp(opts
, "scope")) {
774 pstrcpy(global_scope
,opteq
+1);
776 slprintf(p
, sizeof(pstring
) - (p
- options
) - 1, "%s=%s,", opts
, opteq
+1);
781 if(!strcmp(opts
, "nocaps")) {
782 fprintf(stderr
, "Unhandled option: %s\n", opteq
+1);
784 } else if(!strcmp(opts
, "guest")) {
787 } else if(!strcmp(opts
, "rw")) {
789 } else if(!strcmp(opts
, "ro")) {
792 strncpy(p
, opts
, sizeof(pstring
) - (p
- options
) - 1);
806 *(p
-1) = 0; /* remove trailing , */
807 DEBUG(3,("passthrough options '%s'\n", options
));
811 /****************************************************************************
813 ****************************************************************************/
814 int main(int argc
,char *argv
[])
818 static pstring servicesf
= CONFIGFILE
;
823 /* here we are interactive, even if run from autofs */
824 setup_logging("mount.smbfs",True
);
827 charset_initialise();
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(username
,'%'))) {
836 pstrcpy(password
,p
+1);
838 memset(strchr(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(servicesf
,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 codepage_initialise(lp_client_code_page());
872 if (*workgroup
== 0) {
873 pstrcpy(workgroup
,lp_workgroup());
877 if (!*my_netbios_name
) {
878 pstrcpy(my_netbios_name
, myhostname());
880 strupper(my_netbios_name
);