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 BOOL in_client;
31 extern pstring user_socket_options;
32 extern BOOL append_log;
33 extern fstring remote_machine;
35 static pstring credentials;
36 static pstring my_netbios_name;
37 static pstring password;
38 static pstring username;
39 static pstring workgroup;
40 static pstring mpoint;
41 static pstring service;
42 static pstring options;
44 static struct in_addr dest_ip;
46 static int smb_port = 0;
48 static uid_t mount_uid;
49 static gid_t mount_gid;
51 static unsigned mount_fmask;
52 static unsigned mount_dmask;
54 static void usage(void);
56 static void exit_parent(int sig)
58 /* parent simply exits when child says go... */
62 static void daemonize(void)
67 signal( SIGTERM, exit_parent );
69 if ((child_pid = sys_fork()) < 0) {
70 DEBUG(0,("could not fork\n"));
75 j = waitpid( child_pid, &status, 0 );
77 if( EINTR == errno ) {
84 /* If we get here - the child exited with some error status */
88 signal( SIGTERM, SIG_DFL );
92 static void close_our_files(int client_fd)
97 getrlimit(RLIMIT_NOFILE,&limits);
98 for (i = 0; i< limits.rlim_max; i++) {
105 static void usr1_handler(int x)
111 /*****************************************************
112 return a connection to a server
113 *******************************************************/
114 static struct cli_state *do_connection(char *the_service)
117 struct nmb_name called, calling;
123 if (the_service[0] != '\\' || the_service[1] != '\\') {
128 pstrcpy(server, the_service+2);
129 share = strchr_m(server,'\\');
139 make_nmb_name(&calling, my_netbios_name, 0x0);
140 make_nmb_name(&called , server, 0x20);
144 if (have_ip) ip = dest_ip;
146 /* have to open a new connection */
147 if (!(c=cli_initialise(NULL)) || (cli_set_port(c, smb_port) != smb_port) ||
148 !cli_connect(c, server_n, &ip)) {
149 DEBUG(0,("%d: Connection to %s failed\n", getpid(), server_n));
156 if (!cli_session_request(c, &calling, &called)) {
158 DEBUG(0,("%d: session request to %s failed (%s)\n",
159 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", getpid()));
174 if (!cli_negprot(c)) {
175 DEBUG(0,("%d: protocol negotiation failed\n", 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 getpid(), cli_errstr(c)));
205 DEBUG(0,("Anonymous login successful\n"));
208 DEBUG(4,("%d: session setup ok\n", getpid()));
210 if (!cli_send_tconX(c, share, "?????",
211 password, strlen(password)+1)) {
212 DEBUG(0,("%d: tree connect failed: %s\n",
213 getpid(), cli_errstr(c)));
218 DEBUG(4,("%d: tconx ok\n", 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 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", getpid()));
259 if ((mtab = setmntent(MOUNTED, "r")) == NULL) {
260 DEBUG(0,("%d: Can't open " MOUNTED ": %s\n",
261 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 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 getpid(), MOUNTED_TMP, strerror(errno)));
290 if (rename(MOUNTED_TMP, MOUNTED) < 0) {
291 DEBUG(0,("%d: Cannot rename %s to %s: %s\n",
292 getpid(), MOUNTED, MOUNTED_TMP, strerror(errno)));
296 if (unlink(MOUNTED"~") == -1) {
297 DEBUG(0,("%d: Can't remove "MOUNTED"~", 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 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 pstrcpy(remote_machine, "smbmount"); /* sneaky ... */
379 setup_logging("mount.smbfs", False);
382 DEBUG(0, ("mount.smbfs: entering daemon mode for service %s, pid=%d\n", the_service, getpid()));
387 /* Wait for a signal from smbfs ... but don't continue
388 until we actually get a new connection. */
390 CatchSignal(SIGUSR1, &usr1_handler);
392 DEBUG(2,("mount.smbfs[%d]: got signal, getting new socket\n", getpid()));
393 c = do_connection(the_service);
397 smb_umount(mount_point);
398 DEBUG(2,("mount.smbfs[%d]: exit\n", getpid()));
406 static void init_mount(void)
408 char mount_point[MAXPATHLEN+1];
415 if (realpath(mpoint, mount_point) == NULL) {
416 fprintf(stderr, "Could not resolve mount point %s\n", mpoint);
421 c = do_connection(service);
423 fprintf(stderr,"SMB connection failed\n");
428 Set up to return as a daemon child and wait in the parent
429 until the child say it's ready...
433 pstrcpy(svc2, service);
434 string_replace(svc2, '\\','/');
435 string_replace(svc2, ' ','_');
437 memset(args, 0, sizeof(args[0])*20);
440 args[i++] = "smbmnt";
442 args[i++] = mount_point;
450 slprintf(tmp, sizeof(tmp)-1, "%d", mount_uid);
452 args[i++] = smb_xstrdup(tmp);
455 slprintf(tmp, sizeof(tmp)-1, "%d", mount_gid);
457 args[i++] = smb_xstrdup(tmp);
460 slprintf(tmp, sizeof(tmp)-1, "0%o", mount_fmask);
462 args[i++] = smb_xstrdup(tmp);
465 slprintf(tmp, sizeof(tmp)-1, "0%o", mount_dmask);
467 args[i++] = smb_xstrdup(tmp);
474 if (sys_fork() == 0) {
477 asprintf(&smbmnt_path, "%s/smbmnt", dyn_BINDIR);
479 if (file_exist(smbmnt_path, NULL)) {
480 execv(smbmnt_path, args);
482 "smbfs/init_mount: execv of %s failed. Error was %s.",
483 smbmnt_path, strerror(errno));
485 execvp("smbmnt", args);
487 "smbfs/init_mount: execv of %s failed. Error was %s.",
488 "smbmnt", strerror(errno));
494 if (waitpid(-1, &status, 0) == -1) {
495 fprintf(stderr,"waitpid failed: Error was %s", strerror(errno) );
496 /* FIXME: do some proper error handling */
500 if (WIFEXITED(status) && WEXITSTATUS(status) != 0) {
501 fprintf(stderr,"smbmnt failed: %d\n", WEXITSTATUS(status));
502 /* FIXME: do some proper error handling */
506 /* Ok... This is the rubicon for that mount point... At any point
507 after this, if the connections fail and can not be reconstructed
508 for any reason, we will have to unmount the mount point. There
509 is no exit from the next call...
511 send_fs_socket(service, mount_point, c);
515 /****************************************************************************
516 get a password from a a file or file descriptor
517 exit on failure (from smbclient, move to libsmb or shared .c file?)
518 ****************************************************************************/
519 static void get_password_file(void)
523 BOOL close_it = False;
527 if ((p = getenv("PASSWD_FD")) != NULL) {
528 pstrcpy(spec, "descriptor ");
530 sscanf(p, "%d", &fd);
532 } else if ((p = getenv("PASSWD_FILE")) != NULL) {
533 fd = sys_open(p, O_RDONLY, 0);
536 fprintf(stderr, "Error opening PASSWD_FILE %s: %s\n",
537 spec, strerror(errno));
543 for(p = pass, *p = '\0'; /* ensure that pass is null-terminated */
544 p && p - pass < sizeof(pass);) {
545 switch (read(fd, p, 1)) {
547 if (*p != '\n' && *p != '\0') {
548 *++p = '\0'; /* advance p, and null-terminate pass */
553 *p = '\0'; /* null-terminate it, just in case... */
554 p = NULL; /* then force the loop condition to become false */
557 fprintf(stderr, "Error reading password from file %s: %s\n",
558 spec, "empty password\n");
563 fprintf(stderr, "Error reading password from file %s: %s\n",
564 spec, strerror(errno));
568 pstrcpy(password, pass);
573 /****************************************************************************
574 get username and password from a credentials file
575 exit on failure (from smbclient, move to libsmb or shared .c file?)
576 ****************************************************************************/
577 static void read_credentials_file(char *filename)
582 char *ptr, *val, *param;
584 if ((auth=sys_fopen(filename, "r")) == NULL)
586 /* fail if we can't open the credentials file */
587 DEBUG(0,("ERROR: Unable to open credentials file!\n"));
593 /* get a line from the file */
594 if (!fgets (buf, sizeof(buf), auth))
598 if ((len) && (buf[len-1]=='\n'))
606 /* break up the line into parameter & value.
607 will need to eat a little whitespace possibly */
609 if (!(ptr = strchr (buf, '=')))
614 /* eat leading white space */
615 while ((*val!='\0') && ((*val==' ') || (*val=='\t')))
618 if (strwicmp("password", param) == 0)
620 pstrcpy(password, val);
623 else if (strwicmp("username", param) == 0)
624 pstrcpy(username, val);
626 memset(buf, 0, sizeof(buf));
632 /****************************************************************************
634 ****************************************************************************/
635 static void usage(void)
637 printf("Usage: mount.smbfs service mountpoint [-o options,...]\n");
639 printf("Version %s\n\n",VERSION);
643 username=<arg> SMB username\n\
644 password=<arg> SMB password\n\
645 credentials=<filename> file with username/password\n\
646 netbiosname=<arg> source NetBIOS name\n\
647 uid=<arg> mount uid or username\n\
648 gid=<arg> mount gid or groupname\n\
649 port=<arg> remote SMB port number\n\
650 fmask=<arg> file umask\n\
651 dmask=<arg> directory umask\n\
652 debug=<arg> debug level\n\
653 ip=<arg> destination host or IP address\n\
654 workgroup=<arg> workgroup on destination\n\
655 sockopt=<arg> TCP socket options\n\
656 scope=<arg> NetBIOS scope\n\
657 iocharset=<arg> Linux charset (iso8859-1, utf8)\n\
658 codepage=<arg> server codepage (cp850)\n\
659 ttl=<arg> dircache time to live\n\
660 guest don't prompt for a password\n\
661 ro mount read-only\n\
662 rw mount read-write\n\
664 This command is designed to be run from within /bin/mount by giving\n\
665 the option '-t smbfs'. For example:\n\
666 mount -t smbfs -o username=tridge,password=foobar //fjall/test /data/test\n\
671 /****************************************************************************
672 Argument parsing for mount.smbfs interface
673 mount will call us like this:
674 mount.smbfs device mountpoint -o <options>
676 <options> is never empty, containing at least rw or ro
677 ****************************************************************************/
678 static void parse_mount_smb(int argc, char **argv)
685 extern pstring global_scope;
688 if (argc < 2 || argv[1][0] == '-') {
693 pstrcpy(service, argv[1]);
694 pstrcpy(mpoint, argv[2]);
696 /* Convert any '/' characters in the service name to
698 string_replace(service, '/','\\');
702 opt = getopt(argc, argv, "o:");
711 * option parsing from nfsmount.c (util-linux-2.9u)
713 for (opts = strtok(optarg, ","); opts; opts = strtok(NULL, ",")) {
714 DEBUG(3, ("opts: %s\n", opts));
715 if ((opteq = strchr_m(opts, '='))) {
716 val = atoi(opteq + 1);
719 if (!strcmp(opts, "username") ||
720 !strcmp(opts, "logon")) {
722 pstrcpy(username,opteq+1);
723 if ((lp=strchr_m(username,'%'))) {
725 pstrcpy(password,lp+1);
727 memset(strchr_m(opteq+1,'%')+1,'X',strlen(password));
729 if ((lp=strchr_m(username,'/'))) {
731 pstrcpy(workgroup,lp+1);
733 } else if(!strcmp(opts, "passwd") ||
734 !strcmp(opts, "password")) {
735 pstrcpy(password,opteq+1);
737 memset(opteq+1,'X',strlen(password));
738 } else if(!strcmp(opts, "credentials")) {
739 pstrcpy(credentials,opteq+1);
740 } else if(!strcmp(opts, "netbiosname")) {
741 pstrcpy(my_netbios_name,opteq+1);
742 } else if(!strcmp(opts, "uid")) {
743 mount_uid = nametouid(opteq+1);
744 } else if(!strcmp(opts, "gid")) {
745 mount_gid = nametogid(opteq+1);
746 } else if(!strcmp(opts, "port")) {
748 } else if(!strcmp(opts, "fmask")) {
749 mount_fmask = strtol(opteq+1, NULL, 8);
750 } else if(!strcmp(opts, "dmask")) {
751 mount_dmask = strtol(opteq+1, NULL, 8);
752 } else if(!strcmp(opts, "debug")) {
754 } else if(!strcmp(opts, "ip")) {
755 dest_ip = *interpret_addr2(opteq+1);
756 if (is_zero_ip(dest_ip)) {
757 fprintf(stderr,"Can't resolve address %s\n", opteq+1);
761 } else if(!strcmp(opts, "workgroup")) {
762 pstrcpy(workgroup,opteq+1);
763 } else if(!strcmp(opts, "sockopt")) {
764 pstrcpy(user_socket_options,opteq+1);
765 } else if(!strcmp(opts, "scope")) {
766 pstrcpy(global_scope,opteq+1);
768 slprintf(p, sizeof(pstring) - (p - options) - 1, "%s=%s,", opts, opteq+1);
773 if(!strcmp(opts, "nocaps")) {
774 fprintf(stderr, "Unhandled option: %s\n", opteq+1);
776 } else if(!strcmp(opts, "guest")) {
779 } else if(!strcmp(opts, "rw")) {
781 } else if(!strcmp(opts, "ro")) {
784 strncpy(p, opts, sizeof(pstring) - (p - options) - 1);
798 *(p-1) = 0; /* remove trailing , */
799 DEBUG(3,("passthrough options '%s'\n", options));
803 /****************************************************************************
805 ****************************************************************************/
806 int main(int argc,char *argv[])
814 /* here we are interactive, even if run from autofs */
815 setup_logging("mount.smbfs",True);
817 #if 0 /* JRA - Urban says not needed ? */
818 /* CLI_FORCE_ASCII=false makes smbmount negotiate unicode. The default
819 is to not announce any unicode capabilities as current smbfs does
821 p = getenv("CLI_FORCE_ASCII");
822 if (p && !strcmp(p, "false"))
823 unsetenv("CLI_FORCE_ASCII");
825 setenv("CLI_FORCE_ASCII", "true", 1);
828 in_client = True; /* Make sure that we tell lp_load we are */
830 if (getenv("USER")) {
831 pstrcpy(username,getenv("USER"));
833 if ((p=strchr_m(username,'%'))) {
835 pstrcpy(password,p+1);
837 memset(strchr_m(getenv("USER"),'%')+1,'X',strlen(password));
842 if (getenv("PASSWD")) {
843 pstrcpy(password,getenv("PASSWD"));
847 if (getenv("PASSWD_FD") || getenv("PASSWD_FILE")) {
852 if (*username == 0 && getenv("LOGNAME")) {
853 pstrcpy(username,getenv("LOGNAME"));
856 if (!lp_load(dyn_CONFIGFILE,True,False,False)) {
857 fprintf(stderr, "Can't load %s - run testparm to debug it\n",
861 parse_mount_smb(argc, argv);
863 if (*credentials != 0) {
864 read_credentials_file(credentials);
867 DEBUG(3,("mount.smbfs started (version %s)\n", VERSION));
869 if (*workgroup == 0) {
870 pstrcpy(workgroup,lp_workgroup());
874 if (!*my_netbios_name) {
875 pstrcpy(my_netbios_name, myhostname());
877 strupper(my_netbios_name);