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 (WIFSIGNALLED(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 if (!cli_session_request(c, &calling, &called)) {
162 DEBUG(0,("%d: session request to %s failed (%s)\n",
163 sys_getpid(), called.name, cli_errstr(c)));
165 if ((p=strchr_m(called.name, '.'))) {
169 if (strcmp(called.name, "*SMBSERVER")) {
170 make_nmb_name(&called , "*SMBSERVER", 0x20);
176 DEBUG(4,("%d: session request ok\n", sys_getpid()));
178 if (!cli_negprot(c)) {
179 DEBUG(0,("%d: protocol negotiation failed\n", sys_getpid()));
185 char *pass = getpass("Password: ");
187 pstrcpy(password, pass);
191 /* This should be right for current smbfs. Future versions will support
192 large files as well as unicode and oplocks. */
193 c->capabilities &= ~(CAP_UNICODE | CAP_LARGE_FILES | CAP_NT_SMBS |
194 CAP_NT_FIND | CAP_STATUS32 | CAP_LEVEL_II_OPLOCKS);
195 c->force_dos_errors = True;
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 sys_getpid(), cli_errstr(c)));
209 DEBUG(0,("Anonymous login successful\n"));
212 DEBUG(4,("%d: session setup ok\n", sys_getpid()));
214 if (!cli_send_tconX(c, share, "?????",
215 password, strlen(password)+1)) {
216 DEBUG(0,("%d: tree connect failed: %s\n",
217 sys_getpid(), cli_errstr(c)));
222 DEBUG(4,("%d: tconx ok\n", sys_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 sys_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", sys_getpid()));
263 if ((mtab = setmntent(MOUNTED, "r")) == NULL) {
264 DEBUG(0,("%d: Can't open " MOUNTED ": %s\n",
265 sys_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 sys_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 sys_getpid(), MOUNTED_TMP, strerror(errno)));
294 if (rename(MOUNTED_TMP, MOUNTED) < 0) {
295 DEBUG(0,("%d: Cannot rename %s to %s: %s\n",
296 sys_getpid(), MOUNTED, MOUNTED_TMP, strerror(errno)));
300 if (unlink(MOUNTED"~") == -1) {
301 DEBUG(0,("%d: Can't remove "MOUNTED"~", sys_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 *the_service, 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 sys_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 set_remote_machine_name("smbmount"); /* sneaky ... */
383 setup_logging("mount.smbfs", False);
385 DEBUG(0, ("mount.smbfs: entering daemon mode for service %s, pid=%d\n", the_service, sys_getpid()));
390 /* Wait for a signal from smbfs ... but don't continue
391 until we actually get a new connection. */
393 CatchSignal(SIGUSR1, &usr1_handler);
395 DEBUG(2,("mount.smbfs[%d]: got signal, getting new socket\n", sys_getpid()));
396 c = do_connection(the_service);
400 smb_umount(mount_point);
401 DEBUG(2,("mount.smbfs[%d]: exit\n", sys_getpid()));
409 static void init_mount(void)
411 char mount_point[MAXPATHLEN+1];
418 if (realpath(mpoint, mount_point) == NULL) {
419 fprintf(stderr, "Could not resolve mount point %s\n", mpoint);
424 c = do_connection(service);
426 fprintf(stderr,"SMB connection failed\n");
431 Set up to return as a daemon child and wait in the parent
432 until the child say it's ready...
436 pstrcpy(svc2, service);
437 string_replace(svc2, '\\','/');
438 string_replace(svc2, ' ','_');
440 memset(args, 0, sizeof(args[0])*20);
443 args[i++] = "smbmnt";
445 args[i++] = mount_point;
453 slprintf(tmp, sizeof(tmp)-1, "%d", mount_uid);
455 args[i++] = smb_xstrdup(tmp);
458 slprintf(tmp, sizeof(tmp)-1, "%d", mount_gid);
460 args[i++] = smb_xstrdup(tmp);
463 slprintf(tmp, sizeof(tmp)-1, "0%o", mount_fmask);
465 args[i++] = smb_xstrdup(tmp);
468 slprintf(tmp, sizeof(tmp)-1, "0%o", mount_dmask);
470 args[i++] = smb_xstrdup(tmp);
477 if (sys_fork() == 0) {
480 asprintf(&smbmnt_path, "%s/smbmnt", dyn_BINDIR);
482 if (file_exist(smbmnt_path, NULL)) {
483 execv(smbmnt_path, args);
485 "smbfs/init_mount: execv of %s failed. Error was %s.",
486 smbmnt_path, strerror(errno));
488 execvp("smbmnt", args);
490 "smbfs/init_mount: execv of %s failed. Error was %s.",
491 "smbmnt", strerror(errno));
497 if (waitpid(-1, &status, 0) == -1) {
498 fprintf(stderr,"waitpid failed: Error was %s", strerror(errno) );
499 /* FIXME: do some proper error handling */
503 if (WIFEXITED(status) && WEXITSTATUS(status) != 0) {
504 fprintf(stderr,"smbmnt failed: %d\n", WEXITSTATUS(status));
505 /* FIXME: do some proper error handling */
509 /* Ok... This is the rubicon for that mount point... At any point
510 after this, if the connections fail and can not be reconstructed
511 for any reason, we will have to unmount the mount point. There
512 is no exit from the next call...
514 send_fs_socket(service, mount_point, c);
518 /****************************************************************************
519 get a password from a a file or file descriptor
520 exit on failure (from smbclient, move to libsmb or shared .c file?)
521 ****************************************************************************/
522 static void get_password_file(void)
526 BOOL close_it = False;
530 if ((p = getenv("PASSWD_FD")) != NULL) {
531 pstrcpy(spec, "descriptor ");
533 sscanf(p, "%d", &fd);
535 } else if ((p = getenv("PASSWD_FILE")) != NULL) {
536 fd = sys_open(p, O_RDONLY, 0);
539 fprintf(stderr, "Error opening PASSWD_FILE %s: %s\n",
540 spec, strerror(errno));
546 for(p = pass, *p = '\0'; /* ensure that pass is null-terminated */
547 p && p - pass < sizeof(pass);) {
548 switch (read(fd, p, 1)) {
550 if (*p != '\n' && *p != '\0') {
551 *++p = '\0'; /* advance p, and null-terminate pass */
556 *p = '\0'; /* null-terminate it, just in case... */
557 p = NULL; /* then force the loop condition to become false */
560 fprintf(stderr, "Error reading password from file %s: %s\n",
561 spec, "empty password\n");
566 fprintf(stderr, "Error reading password from file %s: %s\n",
567 spec, strerror(errno));
571 pstrcpy(password, pass);
576 /****************************************************************************
577 get username and password from a credentials file
578 exit on failure (from smbclient, move to libsmb or shared .c file?)
579 ****************************************************************************/
580 static void read_credentials_file(char *filename)
585 char *ptr, *val, *param;
587 if ((auth=sys_fopen(filename, "r")) == NULL)
589 /* fail if we can't open the credentials file */
590 DEBUG(0,("ERROR: Unable to open credentials file!\n"));
596 /* get a line from the file */
597 if (!fgets (buf, sizeof(buf), auth))
601 if ((len) && (buf[len-1]=='\n'))
609 /* break up the line into parameter & value.
610 will need to eat a little whitespace possibly */
612 if (!(ptr = strchr (buf, '=')))
617 /* eat leading white space */
618 while ((*val!='\0') && ((*val==' ') || (*val=='\t')))
621 if (strwicmp("password", param) == 0)
623 pstrcpy(password, val);
626 else if (strwicmp("username", param) == 0)
627 pstrcpy(username, val);
629 memset(buf, 0, sizeof(buf));
635 /****************************************************************************
637 ****************************************************************************/
638 static void usage(void)
640 printf("Usage: mount.smbfs service mountpoint [-o options,...]\n");
642 printf("Version %s\n\n",VERSION);
646 username=<arg> SMB username\n\
647 password=<arg> SMB password\n\
648 credentials=<filename> file with username/password\n\
649 netbiosname=<arg> source NetBIOS name\n\
650 uid=<arg> mount uid or username\n\
651 gid=<arg> mount gid or groupname\n\
652 port=<arg> remote SMB port number\n\
653 fmask=<arg> file umask\n\
654 dmask=<arg> directory umask\n\
655 debug=<arg> debug level\n\
656 ip=<arg> destination host or IP address\n\
657 workgroup=<arg> workgroup on destination\n\
658 sockopt=<arg> TCP socket options\n\
659 scope=<arg> NetBIOS scope\n\
660 iocharset=<arg> Linux charset (iso8859-1, utf8)\n\
661 codepage=<arg> server codepage (cp850)\n\
662 ttl=<arg> dircache time to live\n\
663 guest don't prompt for a password\n\
664 ro mount read-only\n\
665 rw mount read-write\n\
667 This command is designed to be run from within /bin/mount by giving\n\
668 the option '-t smbfs'. For example:\n\
669 mount -t smbfs -o username=tridge,password=foobar //fjall/test /data/test\n\
674 /****************************************************************************
675 Argument parsing for mount.smbfs interface
676 mount will call us like this:
677 mount.smbfs device mountpoint -o <options>
679 <options> is never empty, containing at least rw or ro
680 ****************************************************************************/
681 static void parse_mount_smb(int argc, char **argv)
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 set_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);