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;
32 extern fstring remote_machine;
34 static pstring credentials;
35 static pstring my_netbios_name;
36 static pstring password;
37 static pstring username;
38 static pstring workgroup;
39 static pstring mpoint;
40 static pstring service;
41 static pstring options;
43 static struct in_addr dest_ip;
45 static int smb_port = 0;
47 static uid_t mount_uid;
48 static gid_t mount_gid;
50 static unsigned mount_fmask;
51 static unsigned mount_dmask;
53 static void usage(void);
55 static void exit_parent(int sig)
57 /* parent simply exits when child says go... */
61 static void daemonize(void)
66 signal( SIGTERM, exit_parent );
68 if ((child_pid = sys_fork()) < 0) {
69 DEBUG(0,("could not fork\n"));
74 j = waitpid( child_pid, &status, 0 );
76 if( EINTR == errno ) {
83 /* If we get here - the child exited with some error status */
87 signal( SIGTERM, SIG_DFL );
91 static void close_our_files(int client_fd)
96 getrlimit(RLIMIT_NOFILE,&limits);
97 for (i = 0; i< limits.rlim_max; i++) {
104 static void usr1_handler(int x)
110 /*****************************************************
111 return a connection to a server
112 *******************************************************/
113 static struct cli_state *do_connection(char *the_service)
116 struct nmb_name called, calling;
122 if (the_service[0] != '\\' || the_service[1] != '\\') {
127 pstrcpy(server, the_service+2);
128 share = strchr_m(server,'\\');
138 make_nmb_name(&calling, my_netbios_name, 0x0);
139 make_nmb_name(&called , server, 0x20);
143 if (have_ip) ip = dest_ip;
145 /* have to open a new connection */
146 if (!(c=cli_initialise(NULL)) || (cli_set_port(c, smb_port) != smb_port) ||
147 !cli_connect(c, server_n, &ip)) {
148 DEBUG(0,("%d: Connection to %s failed\n", sys_getpid(), server_n));
155 /* SPNEGO doesn't work till we get NTSTATUS error support */
156 c->use_spnego = False;
158 if (!cli_session_request(c, &calling, &called)) {
160 DEBUG(0,("%d: session request to %s failed (%s)\n",
161 sys_getpid(), called.name, cli_errstr(c)));
163 if ((p=strchr_m(called.name, '.'))) {
167 if (strcmp(called.name, "*SMBSERVER")) {
168 make_nmb_name(&called , "*SMBSERVER", 0x20);
174 DEBUG(4,("%d: session request ok\n", sys_getpid()));
176 if (!cli_negprot(c)) {
177 DEBUG(0,("%d: protocol negotiation failed\n", sys_getpid()));
183 char *pass = getpass("Password: ");
185 pstrcpy(password, pass);
189 /* This should be right for current smbfs. Future versions will support
190 large files as well as unicode and oplocks. */
191 c->capabilities &= ~(CAP_UNICODE | CAP_LARGE_FILES | CAP_NT_SMBS |
192 CAP_NT_FIND | CAP_STATUS32 | CAP_LEVEL_II_OPLOCKS);
193 c->force_dos_errors = True;
194 if (!cli_session_setup(c, username,
195 password, strlen(password),
196 password, strlen(password),
198 /* if a password was not supplied then try again with a
200 if (password[0] || !username[0] ||
201 !cli_session_setup(c, "", "", 0, "", 0, workgroup)) {
202 DEBUG(0,("%d: session setup failed: %s\n",
203 sys_getpid(), cli_errstr(c)));
207 DEBUG(0,("Anonymous login successful\n"));
210 DEBUG(4,("%d: session setup ok\n", sys_getpid()));
212 if (!cli_send_tconX(c, share, "?????",
213 password, strlen(password)+1)) {
214 DEBUG(0,("%d: tree connect failed: %s\n",
215 sys_getpid(), cli_errstr(c)));
220 DEBUG(4,("%d: tconx ok\n", sys_getpid()));
228 /****************************************************************************
229 unmount smbfs (this is a bailout routine to clean up if a reconnect fails)
230 Code blatently stolen from smbumount.c
232 ****************************************************************************/
233 static void smb_umount(char *mount_point)
241 This routine only gets called to the scene of a disaster
242 to shoot the survivors... A connection that was working
243 has now apparently failed. We have an active mount point
244 (presumably) that we need to dump. If we get errors along
245 the way - make some noise, but we are already turning out
246 the lights to exit anyways...
248 if (umount(mount_point) != 0) {
249 DEBUG(0,("%d: Could not umount %s: %s\n",
250 sys_getpid(), mount_point, strerror(errno)));
254 if ((fd = open(MOUNTED"~", O_RDWR|O_CREAT|O_EXCL, 0600)) == -1) {
255 DEBUG(0,("%d: Can't get "MOUNTED"~ lock file", sys_getpid()));
261 if ((mtab = setmntent(MOUNTED, "r")) == NULL) {
262 DEBUG(0,("%d: Can't open " MOUNTED ": %s\n",
263 sys_getpid(), strerror(errno)));
267 #define MOUNTED_TMP MOUNTED".tmp"
269 if ((new_mtab = setmntent(MOUNTED_TMP, "w")) == NULL) {
270 DEBUG(0,("%d: Can't open " MOUNTED_TMP ": %s\n",
271 sys_getpid(), strerror(errno)));
276 while ((mnt = getmntent(mtab)) != NULL) {
277 if (strcmp(mnt->mnt_dir, mount_point) != 0) {
278 addmntent(new_mtab, mnt);
284 if (fchmod (fileno (new_mtab), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) {
285 DEBUG(0,("%d: Error changing mode of %s: %s\n",
286 sys_getpid(), MOUNTED_TMP, strerror(errno)));
292 if (rename(MOUNTED_TMP, MOUNTED) < 0) {
293 DEBUG(0,("%d: Cannot rename %s to %s: %s\n",
294 sys_getpid(), MOUNTED, MOUNTED_TMP, strerror(errno)));
298 if (unlink(MOUNTED"~") == -1) {
299 DEBUG(0,("%d: Can't remove "MOUNTED"~", sys_getpid()));
306 * Call the smbfs ioctl to install a connection socket,
307 * then wait for a signal to reconnect. Note that we do
308 * not exit after open_sockets() or send_login() errors,
309 * as the smbfs mount would then have no way to recover.
311 static void send_fs_socket(char *the_service, char *mount_point, struct cli_state *c)
313 int fd, closed = 0, res = 1;
314 pid_t parentpid = getppid();
315 struct smb_conn_opt conn_options;
317 memset(&conn_options, 0, sizeof(conn_options));
320 if ((fd = open(mount_point, O_RDONLY)) < 0) {
321 DEBUG(0,("mount.smbfs[%d]: can't open %s\n",
322 sys_getpid(), mount_point));
326 conn_options.fd = c->fd;
327 conn_options.protocol = c->protocol;
328 conn_options.case_handling = SMB_CASE_DEFAULT;
329 conn_options.max_xmit = c->max_xmit;
330 conn_options.server_uid = c->vuid;
331 conn_options.tid = c->cnum;
332 conn_options.secmode = c->sec_mode;
333 conn_options.rawmode = 0;
334 conn_options.sesskey = c->sesskey;
335 conn_options.maxraw = 0;
336 conn_options.capabilities = c->capabilities;
337 conn_options.serverzone = c->serverzone/60;
339 res = ioctl(fd, SMB_IOC_NEWCONN, &conn_options);
341 DEBUG(0,("mount.smbfs[%d]: ioctl failed, res=%d\n",
348 /* Ok... We are going to kill the parent. Now
349 is the time to break the process group... */
351 /* Send a signal to the parent to terminate */
352 kill(parentpid, SIGTERM);
358 /* This looks wierd but we are only closing the userspace
359 side, the connection has already been passed to smbfs and
360 it has increased the usage count on the socket.
362 If we don't do this we will "leak" sockets and memory on
363 each reconnection we have to make. */
368 /* redirect stdout & stderr since we can't know that
369 the library functions we use are using DEBUG. */
370 if ( (fd = open("/dev/null", O_WRONLY)) < 0)
371 DEBUG(2,("mount.smbfs: can't open /dev/null\n"));
374 dup2(fd, STDOUT_FILENO);
375 dup2(fd, STDERR_FILENO);
379 /* here we are no longer interactive */
380 pstrcpy(remote_machine, "smbmount"); /* sneaky ... */
381 setup_logging("mount.smbfs", False);
384 DEBUG(0, ("mount.smbfs: entering daemon mode for service %s, pid=%d\n", the_service, sys_getpid()));
389 /* Wait for a signal from smbfs ... but don't continue
390 until we actually get a new connection. */
392 CatchSignal(SIGUSR1, &usr1_handler);
394 DEBUG(2,("mount.smbfs[%d]: got signal, getting new socket\n", sys_getpid()));
395 c = do_connection(the_service);
399 smb_umount(mount_point);
400 DEBUG(2,("mount.smbfs[%d]: exit\n", sys_getpid()));
408 static void init_mount(void)
410 char mount_point[MAXPATHLEN+1];
417 if (realpath(mpoint, mount_point) == NULL) {
418 fprintf(stderr, "Could not resolve mount point %s\n", mpoint);
423 c = do_connection(service);
425 fprintf(stderr,"SMB connection failed\n");
430 Set up to return as a daemon child and wait in the parent
431 until the child say it's ready...
435 pstrcpy(svc2, service);
436 string_replace(svc2, '\\','/');
437 string_replace(svc2, ' ','_');
439 memset(args, 0, sizeof(args[0])*20);
442 args[i++] = "smbmnt";
444 args[i++] = mount_point;
452 slprintf(tmp, sizeof(tmp)-1, "%d", mount_uid);
454 args[i++] = smb_xstrdup(tmp);
457 slprintf(tmp, sizeof(tmp)-1, "%d", mount_gid);
459 args[i++] = smb_xstrdup(tmp);
462 slprintf(tmp, sizeof(tmp)-1, "0%o", mount_fmask);
464 args[i++] = smb_xstrdup(tmp);
467 slprintf(tmp, sizeof(tmp)-1, "0%o", mount_dmask);
469 args[i++] = smb_xstrdup(tmp);
476 if (sys_fork() == 0) {
479 asprintf(&smbmnt_path, "%s/smbmnt", dyn_BINDIR);
481 if (file_exist(smbmnt_path, NULL)) {
482 execv(smbmnt_path, args);
484 "smbfs/init_mount: execv of %s failed. Error was %s.",
485 smbmnt_path, strerror(errno));
487 execvp("smbmnt", args);
489 "smbfs/init_mount: execv of %s failed. Error was %s.",
490 "smbmnt", strerror(errno));
496 if (waitpid(-1, &status, 0) == -1) {
497 fprintf(stderr,"waitpid failed: Error was %s", strerror(errno) );
498 /* FIXME: do some proper error handling */
502 if (WIFEXITED(status) && WEXITSTATUS(status) != 0) {
503 fprintf(stderr,"smbmnt failed: %d\n", WEXITSTATUS(status));
504 /* FIXME: do some proper error handling */
508 /* Ok... This is the rubicon for that mount point... At any point
509 after this, if the connections fail and can not be reconstructed
510 for any reason, we will have to unmount the mount point. There
511 is no exit from the next call...
513 send_fs_socket(service, mount_point, c);
517 /****************************************************************************
518 get a password from a a file or file descriptor
519 exit on failure (from smbclient, move to libsmb or shared .c file?)
520 ****************************************************************************/
521 static void get_password_file(void)
525 BOOL close_it = False;
529 if ((p = getenv("PASSWD_FD")) != NULL) {
530 pstrcpy(spec, "descriptor ");
532 sscanf(p, "%d", &fd);
534 } else if ((p = getenv("PASSWD_FILE")) != NULL) {
535 fd = sys_open(p, O_RDONLY, 0);
538 fprintf(stderr, "Error opening PASSWD_FILE %s: %s\n",
539 spec, strerror(errno));
545 for(p = pass, *p = '\0'; /* ensure that pass is null-terminated */
546 p && p - pass < sizeof(pass);) {
547 switch (read(fd, p, 1)) {
549 if (*p != '\n' && *p != '\0') {
550 *++p = '\0'; /* advance p, and null-terminate pass */
555 *p = '\0'; /* null-terminate it, just in case... */
556 p = NULL; /* then force the loop condition to become false */
559 fprintf(stderr, "Error reading password from file %s: %s\n",
560 spec, "empty password\n");
565 fprintf(stderr, "Error reading password from file %s: %s\n",
566 spec, strerror(errno));
570 pstrcpy(password, pass);
575 /****************************************************************************
576 get username and password from a credentials file
577 exit on failure (from smbclient, move to libsmb or shared .c file?)
578 ****************************************************************************/
579 static void read_credentials_file(char *filename)
584 char *ptr, *val, *param;
586 if ((auth=sys_fopen(filename, "r")) == NULL)
588 /* fail if we can't open the credentials file */
589 DEBUG(0,("ERROR: Unable to open credentials file!\n"));
595 /* get a line from the file */
596 if (!fgets (buf, sizeof(buf), auth))
600 if ((len) && (buf[len-1]=='\n'))
608 /* break up the line into parameter & value.
609 will need to eat a little whitespace possibly */
611 if (!(ptr = strchr (buf, '=')))
616 /* eat leading white space */
617 while ((*val!='\0') && ((*val==' ') || (*val=='\t')))
620 if (strwicmp("password", param) == 0)
622 pstrcpy(password, val);
625 else if (strwicmp("username", param) == 0)
626 pstrcpy(username, val);
628 memset(buf, 0, sizeof(buf));
634 /****************************************************************************
636 ****************************************************************************/
637 static void usage(void)
639 printf("Usage: mount.smbfs service mountpoint [-o options,...]\n");
641 printf("Version %s\n\n",VERSION);
645 username=<arg> SMB username\n\
646 password=<arg> SMB password\n\
647 credentials=<filename> file with username/password\n\
648 netbiosname=<arg> source NetBIOS name\n\
649 uid=<arg> mount uid or username\n\
650 gid=<arg> mount gid or groupname\n\
651 port=<arg> remote SMB port number\n\
652 fmask=<arg> file umask\n\
653 dmask=<arg> directory umask\n\
654 debug=<arg> debug level\n\
655 ip=<arg> destination host or IP address\n\
656 workgroup=<arg> workgroup on destination\n\
657 sockopt=<arg> TCP socket options\n\
658 scope=<arg> NetBIOS scope\n\
659 iocharset=<arg> Linux charset (iso8859-1, utf8)\n\
660 codepage=<arg> server codepage (cp850)\n\
661 ttl=<arg> dircache time to live\n\
662 guest don't prompt for a password\n\
663 ro mount read-only\n\
664 rw mount read-write\n\
666 This command is designed to be run from within /bin/mount by giving\n\
667 the option '-t smbfs'. For example:\n\
668 mount -t smbfs -o username=tridge,password=foobar //fjall/test /data/test\n\
673 /****************************************************************************
674 Argument parsing for mount.smbfs interface
675 mount will call us like this:
676 mount.smbfs device mountpoint -o <options>
678 <options> is never empty, containing at least rw or ro
679 ****************************************************************************/
680 static void parse_mount_smb(int argc, char **argv)
687 extern pstring global_scope;
690 if (argc < 2 || argv[1][0] == '-') {
695 pstrcpy(service, argv[1]);
696 pstrcpy(mpoint, argv[2]);
698 /* Convert any '/' characters in the service name to
700 string_replace(service, '/','\\');
704 opt = getopt(argc, argv, "o:");
713 * option parsing from nfsmount.c (util-linux-2.9u)
715 for (opts = strtok(optarg, ","); opts; opts = strtok(NULL, ",")) {
716 DEBUG(3, ("opts: %s\n", opts));
717 if ((opteq = strchr_m(opts, '='))) {
718 val = atoi(opteq + 1);
721 if (!strcmp(opts, "username") ||
722 !strcmp(opts, "logon")) {
724 pstrcpy(username,opteq+1);
725 if ((lp=strchr_m(username,'%'))) {
727 pstrcpy(password,lp+1);
729 memset(strchr_m(opteq+1,'%')+1,'X',strlen(password));
731 if ((lp=strchr_m(username,'/'))) {
733 pstrcpy(workgroup,lp+1);
735 } else if(!strcmp(opts, "passwd") ||
736 !strcmp(opts, "password")) {
737 pstrcpy(password,opteq+1);
739 memset(opteq+1,'X',strlen(password));
740 } else if(!strcmp(opts, "credentials")) {
741 pstrcpy(credentials,opteq+1);
742 } else if(!strcmp(opts, "netbiosname")) {
743 pstrcpy(my_netbios_name,opteq+1);
744 } else if(!strcmp(opts, "uid")) {
745 mount_uid = nametouid(opteq+1);
746 } else if(!strcmp(opts, "gid")) {
747 mount_gid = nametogid(opteq+1);
748 } else if(!strcmp(opts, "port")) {
750 } else if(!strcmp(opts, "fmask")) {
751 mount_fmask = strtol(opteq+1, NULL, 8);
752 } else if(!strcmp(opts, "dmask")) {
753 mount_dmask = strtol(opteq+1, NULL, 8);
754 } else if(!strcmp(opts, "debug")) {
756 } else if(!strcmp(opts, "ip")) {
757 dest_ip = *interpret_addr2(opteq+1);
758 if (is_zero_ip(dest_ip)) {
759 fprintf(stderr,"Can't resolve address %s\n", opteq+1);
763 } else if(!strcmp(opts, "workgroup")) {
764 pstrcpy(workgroup,opteq+1);
765 } else if(!strcmp(opts, "sockopt")) {
766 pstrcpy(user_socket_options,opteq+1);
767 } else if(!strcmp(opts, "scope")) {
768 pstrcpy(global_scope,opteq+1);
770 slprintf(p, sizeof(pstring) - (p - options) - 1, "%s=%s,", opts, opteq+1);
775 if(!strcmp(opts, "nocaps")) {
776 fprintf(stderr, "Unhandled option: %s\n", opteq+1);
778 } else if(!strcmp(opts, "guest")) {
781 } else if(!strcmp(opts, "rw")) {
783 } else if(!strcmp(opts, "ro")) {
786 strncpy(p, opts, sizeof(pstring) - (p - options) - 1);
800 *(p-1) = 0; /* remove trailing , */
801 DEBUG(3,("passthrough options '%s'\n", options));
805 /****************************************************************************
807 ****************************************************************************/
808 int main(int argc,char *argv[])
816 /* here we are interactive, even if run from autofs */
817 setup_logging("mount.smbfs",True);
819 #if 0 /* JRA - Urban says not needed ? */
820 /* CLI_FORCE_ASCII=false makes smbmount negotiate unicode. The default
821 is to not announce any unicode capabilities as current smbfs does
823 p = getenv("CLI_FORCE_ASCII");
824 if (p && !strcmp(p, "false"))
825 unsetenv("CLI_FORCE_ASCII");
827 setenv("CLI_FORCE_ASCII", "true", 1);
830 in_client = True; /* Make sure that we tell lp_load we are */
832 if (getenv("USER")) {
833 pstrcpy(username,getenv("USER"));
835 if ((p=strchr_m(username,'%'))) {
837 pstrcpy(password,p+1);
839 memset(strchr_m(getenv("USER"),'%')+1,'X',strlen(password));
844 if (getenv("PASSWD")) {
845 pstrcpy(password,getenv("PASSWD"));
849 if (getenv("PASSWD_FD") || getenv("PASSWD_FILE")) {
854 if (*username == 0 && getenv("LOGNAME")) {
855 pstrcpy(username,getenv("LOGNAME"));
858 if (!lp_load(dyn_CONFIGFILE,True,False,False)) {
859 fprintf(stderr, "Can't load %s - run testparm to debug it\n",
863 parse_mount_smb(argc, argv);
865 if (*credentials != 0) {
866 read_credentials_file(credentials);
869 DEBUG(3,("mount.smbfs started (version %s)\n", VERSION));
871 if (*workgroup == 0) {
872 pstrcpy(workgroup,lp_workgroup());
876 if (!*my_netbios_name) {
877 pstrcpy(my_netbios_name, myhostname());
879 strupper(my_netbios_name);