1 /*
2    Unix SMB/CIFS implementation.
3    SMBFS mount program
4    Copyright (C) Andrew Tridgell 1999
5 
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.
10 
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.
15 
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.
19 */
20 
21 #include "includes.h"
22 #include "system/passwd.h"
23 
24 #include <mntent.h>
25 #include <asm/types.h>
26 #include <linux/smb_fs.h>
27 
28 static pstring credentials;
29 static pstring my_netbios_name;
30 static pstring password;
31 static pstring username;
32 static pstring workgroup;
33 static pstring mpoint;
34 static pstring service;
35 static pstring options;
36 
37 static struct ipv4_addr dest_ip;
38 static BOOL have_ip;
39 static int smb_port = 0;
40 static BOOL got_user;
41 static BOOL got_pass;
42 static uid_t mount_uid;
43 static gid_t mount_gid;
44 static int mount_ro;
45 static uint_t mount_fmask;
46 static uint_t mount_dmask;
47 static BOOL use_kerberos;
48 /* TODO: Add code to detect smbfs version in kernel */
49 static BOOL status32_smbfs = False;
50 
51 static void usage(void);
52 
exit_parent(int sig)53 static void exit_parent(int sig)
54 {
55 	/* parent simply exits when child says go... */
56 	exit(0);
57 }
58 
daemonize(void)59 static void daemonize(void)
60 {
61 	int j, status;
62 	pid_t child_pid;
63 
64 	signal( SIGTERM, exit_parent );
65 
66 	if ((child_pid = sys_fork()) < 0) {
67 		DEBUG(0,("could not fork\n"));
68 	}
69 
70 	if (child_pid > 0) {
71 		while( 1 ) {
72 			j = waitpid( child_pid, &status, 0 );
73 			if( j < 0 ) {
74 				if( EINTR == errno ) {
75 					continue;
76 				}
77 				status = errno;
78 			}
79 			break;
80 		}
81 
82 		/* If we get here - the child exited with some error status */
83 		if (WIFSIGNALED(status))
84 			exit(128 + WTERMSIG(status));
85 		else
86 			exit(WEXITSTATUS(status));
87 	}
88 
89 	signal( SIGTERM, SIG_DFL );
90 	chdir("/");
91 }
92 
close_our_files(int client_fd)93 static void close_our_files(int client_fd)
94 {
95 	int i;
96 	struct rlimit limits;
97 
98 	getrlimit(RLIMIT_NOFILE,&limits);
99 	for (i = 0; i< limits.rlim_max; i++) {
100 		if (i == client_fd)
101 			continue;
102 		close(i);
103 	}
104 }
105 
usr1_handler(int x)106 static void usr1_handler(int x)
107 {
108 	return;
109 }
110 
111 
112 /*****************************************************
113 return a connection to a server
114 *******************************************************/
do_connection(char * the_service)115 static struct smbcli_state *do_connection(char *the_service)
116 {
117 	struct smbcli_state *c;
118 	struct nmb_name called, calling;
119 	char *server_n;
120 	struct ipv4_addr ip;
121 	pstring server;
122 	char *share;
123 
124 	if (the_service[0] != '\\' || the_service[1] != '\\') {
125 		usage();
126 		exit(1);
127 	}
128 
129 	pstrcpy(server, the_service+2);
130 	share = strchr_m(server,'\\');
131 	if (!share) {
132 		usage();
133 		exit(1);
134 	}
135 	*share = 0;
136 	share++;
137 
138 	server_n = server;
139 
140 	make_nmb_name(&calling, my_netbios_name, 0x0);
141 	choose_called_name(&called, server, 0x20);
142 
143  again:
144         zero_ip(&ip);
145 	if (have_ip) ip = dest_ip;
146 
147 	/* have to open a new connection */
148 	if (!(c=smbcli_initialise(NULL)) || (smbcli_set_port(c, smb_port) != smb_port) ||
149 	    !smbcli_connect(c, server_n, &ip)) {
150 		DEBUG(0,("%d: Connection to %s failed\n", sys_getpid(), server_n));
151 		if (c) {
152 			talloc_free(c);
153 		}
154 		return NULL;
155 	}
156 
157 	/* SPNEGO doesn't work till we get NTSTATUS error support */
158 	/* But it is REQUIRED for kerberos authentication */
159 	if(!use_kerberos) c->use_spnego = False;
160 
161 	/* The kernel doesn't yet know how to sign it's packets */
162 	c->sign_info.allow_smb_signing = False;
163 
164 	/* Use kerberos authentication if specified */
165 	c->use_kerberos = use_kerberos;
166 
167 	if (!smbcli_session_request(c, &calling, &called)) {
168 		char *p;
169 		DEBUG(0,("%d: session request to %s failed (%s)\n",
170 			 sys_getpid(), called.name, smbcli_errstr(c)));
171 		talloc_free(c);
172 		if ((p=strchr_m(called.name, '.'))) {
173 			*p = 0;
174 			goto again;
175 		}
176 		if (strcmp(called.name, "*SMBSERVER")) {
177 			make_nmb_name(&called , "*SMBSERVER", 0x20);
178 			goto again;
179 		}
180 		return NULL;
181 	}
182 
183 	DEBUG(4,("%d: session request ok\n", sys_getpid()));
184 
185 	if (!smbcli_negprot(c)) {
186 		DEBUG(0,("%d: protocol negotiation failed\n", sys_getpid()));
187 		talloc_free(c);
188 		return NULL;
189 	}
190 
191 	if (!got_pass) {
192 		char *pass = getpass("Password: ");
193 		if (pass) {
194 			pstrcpy(password, pass);
195 		}
196 	}
197 
198 	/* This should be right for current smbfs. Future versions will support
199 	  large files as well as unicode and oplocks. */
200 	if (status32_smbfs) {
201 	    c->capabilities &= ~(CAP_UNICODE | CAP_LARGE_FILES | CAP_NT_SMBS |
202                                  CAP_NT_FIND | CAP_LEVEL_II_OPLOCKS);
203 	}
204 	else {
205 	    c->capabilities &= ~(CAP_UNICODE | CAP_LARGE_FILES | CAP_NT_SMBS |
206 				 CAP_NT_FIND | CAP_STATUS32 |
207 				 CAP_LEVEL_II_OPLOCKS);
208 	    c->force_dos_errors = True;
209 	}
210 
211 	if (!smbcli_session_setup(c, username,
212 			       password, strlen(password),
213 			       password, strlen(password),
214 			       workgroup)) {
215 		/* if a password was not supplied then try again with a
216 			null username */
217 		if (password[0] || !username[0] ||
218 				!smbcli_session_setup(c, "", "", 0, "", 0, workgroup)) {
219 			DEBUG(0,("%d: session setup failed: %s\n",
220 				sys_getpid(), smbcli_errstr(c)));
221 			talloc_free(c);
222 			return NULL;
223 		}
224 		DEBUG(0,("Anonymous login successful\n"));
225 	}
226 
227 	DEBUG(4,("%d: session setup ok\n", sys_getpid()));
228 
229 	if (!smbcli_tconX(c, share, "?????", password, strlen(password)+1)) {
230 		DEBUG(0,("%d: tree connect failed: %s\n",
231 			 sys_getpid(), smbcli_errstr(c)));
232 		talloc_free(c);
233 		return NULL;
234 	}
235 
236 	DEBUG(4,("%d: tconx ok\n", sys_getpid()));
237 
238 	got_pass = True;
239 
240 	return c;
241 }
242 
243 
244 /****************************************************************************
245 unmount smbfs  (this is a bailout routine to clean up if a reconnect fails)
246 	Code blatently stolen from smbumount.c
247 		-mhw-
248 ****************************************************************************/
smb_umount(char * mount_point)249 static void smb_umount(char *mount_point)
250 {
251 	int fd;
252         struct mntent *mnt;
253         FILE* mtab;
254         FILE* new_mtab;
255 
256 	/* Programmers Note:
257 		This routine only gets called to the scene of a disaster
258 		to shoot the survivors...  A connection that was working
259 		has now apparently failed.  We have an active mount point
260 		(presumably) that we need to dump.  If we get errors along
261 		the way - make some noise, but we are already turning out
262 		the lights to exit anyways...
263 	*/
264         if (umount(mount_point) != 0) {
265                 DEBUG(0,("%d: Could not umount %s: %s\n",
266 			 sys_getpid(), mount_point, strerror(errno)));
267                 return;
268         }
269 
270         if ((fd = open(MOUNTED"~", O_RDWR|O_CREAT|O_EXCL, 0600)) == -1) {
271                 DEBUG(0,("%d: Can't get "MOUNTED"~ lock file", sys_getpid()));
272                 return;
273         }
274 
275         close(fd);
276 
277         if ((mtab = setmntent(MOUNTED, "r")) == NULL) {
278                 DEBUG(0,("%d: Can't open " MOUNTED ": %s\n",
279 			 sys_getpid(), strerror(errno)));
280                 return;
281         }
282 
283 #define MOUNTED_TMP MOUNTED".tmp"
284 
285         if ((new_mtab = setmntent(MOUNTED_TMP, "w")) == NULL) {
286                 DEBUG(0,("%d: Can't open " MOUNTED_TMP ": %s\n",
287 			 sys_getpid(), strerror(errno)));
288                 endmntent(mtab);
289                 return;
290         }
291 
292         while ((mnt = getmntent(mtab)) != NULL) {
293                 if (strcmp(mnt->mnt_dir, mount_point) != 0) {
294                         addmntent(new_mtab, mnt);
295                 }
296         }
297 
298         endmntent(mtab);
299 
300         if (fchmod (fileno (new_mtab), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) {
301                 DEBUG(0,("%d: Error changing mode of %s: %s\n",
302 			 sys_getpid(), MOUNTED_TMP, strerror(errno)));
303                 return;
304         }
305 
306         endmntent(new_mtab);
307 
308         if (rename(MOUNTED_TMP, MOUNTED) < 0) {
309                 DEBUG(0,("%d: Cannot rename %s to %s: %s\n",
310 			 sys_getpid(), MOUNTED, MOUNTED_TMP, strerror(errno)));
311                 return;
312         }
313 
314         if (unlink(MOUNTED"~") == -1) {
315                 DEBUG(0,("%d: Can't remove "MOUNTED"~", sys_getpid()));
316                 return;
317         }
318 }
319 
320 
321 /*
322  * Call the smbfs ioctl to install a connection socket,
323  * then wait for a signal to reconnect. Note that we do
324  * not exit after open_sockets() or send_login() errors,
325  * as the smbfs mount would then have no way to recover.
326  */
send_fs_socket(char * the_service,char * mount_point,struct smbcli_state * c)327 static void send_fs_socket(char *the_service, char *mount_point, struct smbcli_state *c)
328 {
329 	int fd, closed = 0, res = 1;
330 	pid_t parentpid = getppid();
331 	struct smb_conn_opt conn_options;
332 
333 	memset(&conn_options, 0, sizeof(conn_options));
334 
335 	while (1) {
336 		if ((fd = open(mount_point, O_RDONLY)) < 0) {
337 			DEBUG(0,("mount.smbfs[%d]: can't open %s\n",
338 				 sys_getpid(), mount_point));
339 			break;
340 		}
341 
342 		conn_options.fd = c->fd;
343 		conn_options.protocol = c->protocol;
344 		conn_options.case_handling = SMB_CASE_DEFAULT;
345 		conn_options.max_xmit = c->max_xmit;
346 		conn_options.server_uid = c->vuid;
347 		conn_options.tid = c->cnum;
348 		conn_options.secmode = c->sec_mode;
349 		conn_options.rawmode = 0;
350 		conn_options.sesskey = c->sesskey;
351 		conn_options.maxraw = 0;
352 		conn_options.capabilities = c->capabilities;
353 		conn_options.serverzone = c->serverzone/60;
354 
355 		res = ioctl(fd, SMB_IOC_NEWCONN, &conn_options);
356 		if (res != 0) {
357 			DEBUG(0,("mount.smbfs[%d]: ioctl failed, res=%d\n",
358 				 sys_getpid(), res));
359 			close(fd);
360 			break;
361 		}
362 
363 		if (parentpid) {
364 			/* Ok...  We are going to kill the parent.  Now
365 				is the time to break the process group... */
366 			setsid();
367 			/* Send a signal to the parent to terminate */
368 			kill(parentpid, SIGTERM);
369 			parentpid = 0;
370 		}
371 
372 		close(fd);
373 
374 		/* This looks wierd but we are only closing the userspace
375 		   side, the connection has already been passed to smbfs and
376 		   it has increased the usage count on the socket.
377 
378 		   If we don't do this we will "leak" sockets and memory on
379 		   each reconnection we have to make. */
380 		talloc_free(c);
381 		c = NULL;
382 
383 		if (!closed) {
384 			/* redirect stdout & stderr since we can't know that
385 			   the library functions we use are using DEBUG. */
386 			if ( (fd = open("/dev/null", O_WRONLY)) < 0)
387 				DEBUG(2,("mount.smbfs: can't open /dev/null\n"));
388 			close_our_files(fd);
389 			if (fd >= 0) {
390 				dup2(fd, STDOUT_FILENO);
391 				dup2(fd, STDERR_FILENO);
392 				close(fd);
393 			}
394 
395 			/* here we are no longer interactive */
396 			set_remote_machine_name("smbmount");	/* sneaky ... */
397 			setup_logging("mount.smbfs", DEBUG_STDERR);
398 			reopen_logs();
399 			DEBUG(0, ("mount.smbfs: entering daemon mode for service %s, pid=%d\n", the_service, sys_getpid()));
400 
401 			closed = 1;
402 		}
403 
404 		/* Wait for a signal from smbfs ... but don't continue
405                    until we actually get a new connection. */
406 		while (!c) {
407 			CatchSignal(SIGUSR1, &usr1_handler);
408 			pause();
409 			DEBUG(2,("mount.smbfs[%d]: got signal, getting new socket\n", sys_getpid()));
410 			c = do_connection(the_service);
411 		}
412 	}
413 
414 	smb_umount(mount_point);
415 	DEBUG(2,("mount.smbfs[%d]: exit\n", sys_getpid()));
416 	exit(1);
417 }
418 
419 
420 /**
421  * Mount a smbfs
422  **/
init_mount(void)423 static void init_mount(void)
424 {
425 	char mount_point[MAXPATHLEN+1];
426 	pstring tmp;
427 	pstring svc2;
428 	struct smbcli_state *c;
429 	char *args[20];
430 	int i, status;
431 
432 	if (realpath(mpoint, mount_point) == NULL) {
433 		fprintf(stderr, "Could not resolve mount point %s\n", mpoint);
434 		return;
435 	}
436 
437 
438 	c = do_connection(service);
439 	if (!c) {
440 		fprintf(stderr,"SMB connection failed\n");
441 		exit(1);
442 	}
443 
444 	/*
445 		Set up to return as a daemon child and wait in the parent
446 		until the child say it's ready...
447 	*/
448 	daemonize();
449 
450 	pstrcpy(svc2, service);
451 	string_replace(svc2, '\\','/');
452 	string_replace(svc2, ' ','_');
453 
454 	memset(args, 0, sizeof(args[0])*20);
455 
456 	i=0;
457 	args[i++] = "smbmnt";
458 
459 	args[i++] = mount_point;
460 	args[i++] = "-s";
461 	args[i++] = svc2;
462 
463 	if (mount_ro) {
464 		args[i++] = "-r";
465 	}
466 	if (mount_uid) {
467 		slprintf(tmp, sizeof(tmp)-1, "%d", mount_uid);
468 		args[i++] = "-u";
469 		args[i++] = smb_xstrdup(tmp);
470 	}
471 	if (mount_gid) {
472 		slprintf(tmp, sizeof(tmp)-1, "%d", mount_gid);
473 		args[i++] = "-g";
474 		args[i++] = smb_xstrdup(tmp);
475 	}
476 	if (mount_fmask) {
477 		slprintf(tmp, sizeof(tmp)-1, "0%o", mount_fmask);
478 		args[i++] = "-f";
479 		args[i++] = smb_xstrdup(tmp);
480 	}
481 	if (mount_dmask) {
482 		slprintf(tmp, sizeof(tmp)-1, "0%o", mount_dmask);
483 		args[i++] = "-d";
484 		args[i++] = smb_xstrdup(tmp);
485 	}
486 	if (options) {
487 		args[i++] = "-o";
488 		args[i++] = options;
489 	}
490 
491 	if (sys_fork() == 0) {
492 		char *smbmnt_path;
493 
494 		asprintf(&smbmnt_path, "%s/smbmnt", dyn_BINDIR);
495 
496 		if (file_exist(smbmnt_path)) {
497 			execv(smbmnt_path, args);
498 			fprintf(stderr,
499 				"smbfs/init_mount: execv of %s failed. Error was %s.",
500 				smbmnt_path, strerror(errno));
501 		} else {
502 			execvp("smbmnt", args);
503 			fprintf(stderr,
504 				"smbfs/init_mount: execv of %s failed. Error was %s.",
505 				"smbmnt", strerror(errno));
506 		}
507 		free(smbmnt_path);
508 		exit(1);
509 	}
510 
511 	if (waitpid(-1, &status, 0) == -1) {
512 		fprintf(stderr,"waitpid failed: Error was %s", strerror(errno) );
513 		/* FIXME: do some proper error handling */
514 		exit(1);
515 	}
516 
517 	if (WIFEXITED(status) && WEXITSTATUS(status) != 0) {
518 		fprintf(stderr,"smbmnt failed: %d\n", WEXITSTATUS(status));
519 		/* FIXME: do some proper error handling */
520 		exit(1);
521 	} else if (WIFSIGNALED(status)) {
522 		fprintf(stderr, "smbmnt killed by signal %d\n", WTERMSIG(status));
523 		exit(1);
524 	}
525 
526 	/* Ok...  This is the rubicon for that mount point...  At any point
527 	   after this, if the connections fail and can not be reconstructed
528 	   for any reason, we will have to unmount the mount point.  There
529 	   is no exit from the next call...
530 	*/
531 	send_fs_socket(service, mount_point, c);
532 }
533 
534 
535 /****************************************************************************
536 get a password from a a file or file descriptor
537 exit on failure (from smbclient, move to libsmb or shared .c file?)
538 ****************************************************************************/
get_password_file(void)539 static void get_password_file(void)
540 {
541 	int fd = -1;
542 	char *p;
543 	BOOL close_it = False;
544 	pstring spec;
545 	char pass[128];
546 
547 	if ((p = getenv("PASSWD_FD")) != NULL) {
548 		pstrcpy(spec, "descriptor ");
549 		pstrcat(spec, p);
550 		sscanf(p, "%d", &fd);
551 		close_it = False;
552 	} else if ((p = getenv("PASSWD_FILE")) != NULL) {
553 		fd = open(p, O_RDONLY, 0);
554 		pstrcpy(spec, p);
555 		if (fd < 0) {
556 			fprintf(stderr, "Error opening PASSWD_FILE %s: %s\n",
557 				spec, strerror(errno));
558 			exit(1);
559 		}
560 		close_it = True;
561 	}
562 
563 	for(p = pass, *p = '\0'; /* ensure that pass is null-terminated */
564 	    p && p - pass < sizeof(pass);) {
565 		switch (read(fd, p, 1)) {
566 		case 1:
567 			if (*p != '\n' && *p != '\0') {
568 				*++p = '\0'; /* advance p, and null-terminate pass */
569 				break;
570 			}
571 		case 0:
572 			if (p - pass) {
573 				*p = '\0'; /* null-terminate it, just in case... */
574 				p = NULL; /* then force the loop condition to become false */
575 				break;
576 			} else {
577 				fprintf(stderr, "Error reading password from file %s: %s\n",
578 					spec, "empty password\n");
579 				exit(1);
580 			}
581 
582 		default:
583 			fprintf(stderr, "Error reading password from file %s: %s\n",
584 				spec, strerror(errno));
585 			exit(1);
586 		}
587 	}
588 	pstrcpy(password, pass);
589 	if (close_it)
590 		close(fd);
591 }
592 
593 /****************************************************************************
594 get username and password from a credentials file
595 exit on failure (from smbclient, move to libsmb or shared .c file?)
596 ****************************************************************************/
read_credentials_file(char * filename)597 static void read_credentials_file(char *filename)
598 {
599 	FILE *auth;
600 	fstring buf;
601 	uint16_t len = 0;
602 	char *ptr, *val, *param;
603 
604 	if ((auth=sys_fopen(filename, "r")) == NULL)
605 	{
606 		/* fail if we can't open the credentials file */
607 		DEBUG(0,("ERROR: Unable to open credentials file!\n"));
608 		exit (-1);
609 	}
610 
611 	while (!feof(auth))
612 	{
613 		/* get a line from the file */
614 		if (!fgets (buf, sizeof(buf), auth))
615 			continue;
616 		len = strlen(buf);
617 
618 		if ((len) && (buf[len-1]=='\n'))
619 		{
620 			buf[len-1] = '\0';
621 			len--;
622 		}
623 		if (len == 0)
624 			continue;
625 
626 		/* break up the line into parameter & value.
627 		   will need to eat a little whitespace possibly */
628 		param = buf;
629 		if (!(ptr = strchr (buf, '=')))
630 			continue;
631 		val = ptr+1;
632 		*ptr = '\0';
633 
634 		/* eat leading white space */
635 		while ((*val!='\0') && ((*val==' ') || (*val=='\t')))
636 			val++;
637 
638 		if (strwicmp("password", param) == 0)
639 		{
640 			pstrcpy(password, val);
641 			got_pass = True;
642 		}
643 		else if (strwicmp("username", param) == 0) {
644 			pstrcpy(username, val);
645 		}
646 
647 		memset(buf, 0, sizeof(buf));
648 	}
649 	fclose(auth);
650 }
651 
652 
653 /****************************************************************************
654 usage on the program
655 ****************************************************************************/
usage(void)656 static void usage(void)
657 {
658 	printf("Usage: mount.smbfs service mountpoint [-o options,...]\n");
659 
660 	printf("Version %s\n\n",VERSION);
661 
662 	printf(
663 "Options:\n\
664       username=<arg>                  SMB username\n\
665       password=<arg>                  SMB password\n\
666       credentials=<filename>          file with username/password\n\
667       krb                             use kerberos (active directory)\n\
668       netbiosname=<arg>               source NetBIOS name\n\
669       uid=<arg>                       mount uid or username\n\
670       gid=<arg>                       mount gid or groupname\n\
671       port=<arg>                      remote SMB port number\n\
672       fmask=<arg>                     file umask\n\
673       dmask=<arg>                     directory umask\n\
674       debug=<arg>                     debug level\n\
675       ip=<arg>                        destination host or IP address\n\
676       workgroup=<arg>                 workgroup on destination\n\
677       sockopt=<arg>                   TCP socket options\n\
678       scope=<arg>                     NetBIOS scope\n\
679       iocharset=<arg>                 Linux charset (iso8859-1, utf8)\n\
680       codepage=<arg>                  server codepage (cp850)\n\
681       ttl=<arg>                       dircache time to live\n\
682       guest                           don't prompt for a password\n\
683       ro                              mount read-only\n\
684       rw                              mount read-write\n\
685 \n\
686 This command is designed to be run from within /bin/mount by giving\n\
687 the option '-t smbfs'. For example:\n\
688   mount -t smbfs -o username=tridge,password=foobar //fjall/test /data/test\n\
689 ");
690 }
691 
692 
693 /****************************************************************************
694   Argument parsing for mount.smbfs interface
695   mount will call us like this:
696     mount.smbfs device mountpoint -o <options>
697 
698   <options> is never empty, containing at least rw or ro
699  ****************************************************************************/
parse_mount_smb(int argc,char ** argv)700 static void parse_mount_smb(int argc, char **argv)
701 {
702 	int opt;
703 	char *opts;
704 	char *opteq;
705 	extern char *optarg;
706 	int val;
707 	char *p;
708 
709 	/* FIXME: This function can silently fail if the arguments are
710 	 * not in the expected order.
711 
712 	> The arguments syntax of smbmount 2.2.3a (smbfs of Debian stable)
713 	> requires that one gives "-o" before further options like username=...
714 	> . Without -o, the username=.. setting is *silently* ignored. I've
715 	> spent about an hour trying to find out why I couldn't log in now..
716 
717 	*/
718 
719 
720 	if (argc < 2 || argv[1][0] == '-') {
721 		usage();
722 		exit(1);
723 	}
724 
725 	pstrcpy(service, argv[1]);
726 	pstrcpy(mpoint, argv[2]);
727 
728 	/* Convert any '/' characters in the service name to
729 	   '\' characters */
730 	string_replace(service, '/','\\');
731 	argc -= 2;
732 	argv += 2;
733 
734 	opt = getopt(argc, argv, "o:");
735 	if(opt != 'o') {
736 		return;
737 	}
738 
739 	options[0] = 0;
740 	p = options;
741 
742 	/*
743 	 * option parsing from nfsmount.c (util-linux-2.9u)
744 	 */
745         for (opts = strtok(optarg, ","); opts; opts = strtok(NULL, ",")) {
746 		DEBUG(3, ("opts: %s\n", opts));
747                 if ((opteq = strchr_m(opts, '='))) {
748                         val = atoi(opteq + 1);
749                         *opteq = '\0';
750 
751                         if (!strcmp(opts, "username") ||
752 			    !strcmp(opts, "logon")) {
753 				char *lp;
754 				got_user = True;
755 				pstrcpy(username,opteq+1);
756 				if ((lp=strchr_m(username,'%'))) {
757 					*lp = 0;
758 					pstrcpy(password,lp+1);
759 					got_pass = True;
760 					memset(strchr_m(opteq+1,'%')+1,'X',strlen(password));
761 				}
762 				if ((lp=strchr_m(username,'/'))) {
763 					*lp = 0;
764 					pstrcpy(workgroup,lp+1);
765 				}
766 			} else if(!strcmp(opts, "passwd") ||
767 				  !strcmp(opts, "password")) {
768 				pstrcpy(password,opteq+1);
769 				got_pass = True;
770 				memset(opteq+1,'X',strlen(password));
771 			} else if(!strcmp(opts, "credentials")) {
772 				pstrcpy(credentials,opteq+1);
773 			} else if(!strcmp(opts, "netbiosname")) {
774 				pstrcpy(my_netbios_name,opteq+1);
775 			} else if(!strcmp(opts, "uid")) {
776 				mount_uid = nametouid(opteq+1);
777 			} else if(!strcmp(opts, "gid")) {
778 				mount_gid = nametogid(opteq+1);
779 			} else if(!strcmp(opts, "port")) {
780 				smb_port = val;
781 			} else if(!strcmp(opts, "fmask")) {
782 				mount_fmask = strtol(opteq+1, NULL, 8);
783 			} else if(!strcmp(opts, "dmask")) {
784 				mount_dmask = strtol(opteq+1, NULL, 8);
785 			} else if(!strcmp(opts, "debug")) {
786 				DEBUGLEVEL = val;
787 			} else if(!strcmp(opts, "ip")) {
788 				dest_ip = interpret_addr2(opteq+1);
789 				if (is_zero_ip(dest_ip)) {
790 					fprintf(stderr,"Can't resolve address %s\n", opteq+1);
791 					exit(1);
792 				}
793 				have_ip = True;
794 			} else if(!strcmp(opts, "workgroup")) {
795 				pstrcpy(workgroup,opteq+1);
796 			} else if(!strcmp(opts, "sockopt")) {
797 				lp_set_cmdline("socket options", opteq+1);
798 			} else if(!strcmp(opts, "scope")) {
799 				lp_set_cmdline("netbios scope", opteq+1);
800 			} else {
801 				slprintf(p, sizeof(pstring) - (p - options) - 1, "%s=%s,", opts, opteq+1);
802 				p += strlen(p);
803 			}
804 		} else {
805 			val = 1;
806 			if(!strcmp(opts, "nocaps")) {
807 				fprintf(stderr, "Unhandled option: %s\n", opteq+1);
808 				exit(1);
809 			} else if(!strcmp(opts, "guest")) {
810 				*password = '\0';
811 				got_pass = True;
812 			} else if(!strcmp(opts, "krb")) {
813 #ifdef HAVE_KRB5
814 
815 				use_kerberos = True;
816 				if(!status32_smbfs)
817 					fprintf(stderr, "Warning: kerberos support will only work for samba servers\n");
818 #else
819 				fprintf(stderr,"No kerberos support compiled in\n");
820 				exit(1);
821 #endif
822 			} else if(!strcmp(opts, "rw")) {
823 				mount_ro = 0;
824 			} else if(!strcmp(opts, "ro")) {
825 				mount_ro = 1;
826 			} else {
827 				strncpy(p, opts, sizeof(pstring) - (p - options) - 1);
828 				p += strlen(opts);
829 				*p++ = ',';
830 				*p = 0;
831 			}
832 		}
833 	}
834 
835 	if (!*service) {
836 		usage();
837 		exit(1);
838 	}
839 
840 	if (p != options) {
841 		*(p-1) = 0;	/* remove trailing , */
842 		DEBUG(3,("passthrough options '%s'\n", options));
843 	}
844 }
845 
846 /****************************************************************************
847   main program
848 ****************************************************************************/
main(int argc,char * argv[])849  int main(int argc,char *argv[])
850 {
851 	extern char *optarg;
852 	extern int optind;
853 	char *p;
854 
855 	DEBUGLEVEL = 1;
856 
857 	/* here we are interactive, even if run from autofs */
858 	setup_logging("mount.smbfs",DEBUG_STDERR);
859 
860 #if 0 /* JRA - Urban says not needed ? */
861 	/* CLI_FORCE_ASCII=false makes smbmount negotiate unicode. The default
862 	   is to not announce any unicode capabilities as current smbfs does
863 	   not support it. */
864 	p = getenv("CLI_FORCE_ASCII");
865 	if (p && !strcmp(p, "false"))
866 		unsetenv("CLI_FORCE_ASCII");
867 	else
868 		setenv("CLI_FORCE_ASCII", "true", 1);
869 #endif
870 
871 	if (getenv("USER")) {
872 		pstrcpy(username,getenv("USER"));
873 
874 		if ((p=strchr_m(username,'%'))) {
875 			*p = 0;
876 			pstrcpy(password,p+1);
877 			got_pass = True;
878 			memset(strchr_m(getenv("USER"),'%')+1,'X',strlen(password));
879 		}
880 		strupper(username);
881 	}
882 
883 	if (getenv("PASSWD")) {
884 		pstrcpy(password,getenv("PASSWD"));
885 		got_pass = True;
886 	}
887 
888 	if (getenv("PASSWD_FD") || getenv("PASSWD_FILE")) {
889 		get_password_file();
890 		got_pass = True;
891 	}
892 
893 	if (*username == 0 && getenv("LOGNAME")) {
894 		pstrcpy(username,getenv("LOGNAME"));
895 	}
896 
897 	if (!lp_load()) {
898 		fprintf(stderr, "Can't load %s - run testparm to debug it\n",
899 			lp_config_file());
900 	}
901 
902 	parse_mount_smb(argc, argv);
903 
904 	if (use_kerberos && !got_user) {
905 		got_pass = True;
906 	}
907 
908 	if (*credentials != 0) {
909 		read_credentials_file(credentials);
910 	}
911 
912 	DEBUG(3,("mount.smbfs started (version %s)\n", VERSION));
913 
914 	if (*workgroup == 0) {
915 		pstrcpy(workgroup,lp_workgroup());
916 	}
917 
918 	if (!*my_netbios_name) {
919 		pstrcpy(my_netbios_name, myhostname());
920 	}
921 	strupper(my_netbios_name);
922 
923 	init_mount();
924 	return 0;
925 }
926