2 Mount helper utility for Linux CIFS VFS (virtual filesystem) client
3 Copyright (C) 2003 Steve French (sfrench@us.ibm.com)
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
27 #include <sys/types.h>
28 #include <sys/mount.h>
30 #include <sys/utsname.h>
31 #include <sys/socket.h>
32 #include <arpa/inet.h>
40 #define MOUNT_CIFS_VERSION_MAJOR "1"
41 #define MOUNT_CIFS_VERSION_MINOR "1"
43 #ifndef MOUNT_CIFS_VENDOR_SUFFIX
44 #define MOUNT_CIFS_VENDOR_SUFFIX ""
53 static int got_password = 0;
54 static int got_user = 0;
55 static int got_domain = 0;
56 static int got_ip = 0;
57 static int got_unc = 0;
58 static int got_uid = 0;
59 static int got_gid = 0;
60 static char * user_name = NULL;
61 char * mountpassword = NULL;
67 open nofollow - avoid symlink exposure?
68 get owner of dir see if matches self or if root
69 call system(umount argv) etc.
73 static void mount_cifs_usage(void)
75 printf("\nUsage: %s <remotetarget> <dir> -o <options>\n", thisprogram);
76 printf("\nMount the remote target, specified as a UNC name,");
77 printf(" to a local directory.\n");
79 memset(mountpassword,0,64);
85 /* caller frees username if necessary */
86 static char * getusername(void) {
87 char *username = NULL;
88 struct passwd *password = getpwuid(getuid());
91 username = password->pw_name;
96 char * parse_cifs_url(char * unc_name)
98 printf("\ncifs url %s\n",unc_name);
102 static int open_cred_file(char * file_name)
108 fs = fopen(file_name,"r");
111 line_buf = malloc(4096);
115 while(fgets(line_buf,4096,fs)) {
116 /* parse line from credential file */
118 /* eat leading white space */
119 for(i=0;i<4096;i++) {
120 if(line_buf[i] == '\0')
122 else if((line_buf[i] != ' ') && (line_buf[i] != '\t'))
127 if (strncasecmp("username",line_buf,8) == 0) {
128 temp_val = strchr(line_buf + i,'=');
130 /* go past equals sign */
132 length = strlen(temp_val);
134 printf("cifs.mount failed due to malformed username in credentials file");
135 memset(line_buf,0,4096);
137 memset(mountpassword,0,64);
142 user_name = calloc(1 + length,1);
143 /* BB adding free of user_name string before exit,
144 not really necessary but would be cleaner */
145 strncpy(user_name,temp_val, length);
148 } else if (strncasecmp("password",line_buf,8) == 0) {
149 temp_val = strchr(line_buf+i,'=');
151 /* go past equals sign */
153 length = strlen(temp_val);
155 printf("cifs.mount failed: password in credentials file too long\n");
156 memset(line_buf,0, 4096);
158 memset(mountpassword,0,64);
162 if(mountpassword == NULL) {
163 mountpassword = calloc(65,1);
166 strncpy(mountpassword,temp_val,64);
175 memset(line_buf,0,4096);
181 static int get_password_from_file(int file_descript, char * filename)
187 if(mountpassword == NULL)
188 mountpassword = calloc(65,1);
190 memset(mountpassword, 0, 64);
192 if(filename != NULL) {
193 file_descript = open(filename, O_RDONLY);
194 if(file_descript < 0) {
195 printf("cifs.mount failed. %s attempting to open password file %s\n",
196 strerror(errno),filename);
200 /* else file already open and fd provided */
203 rc = read(file_descript,&c,1);
205 printf("cifs.mount failed. Error %s reading password file\n",strerror(errno));
206 memset(mountpassword,0,64);
208 close(file_descript);
211 if(mountpassword[0] == 0) {
213 printf("\nWarning: null password used since cifs password file empty");
216 } else /* read valid character */ {
217 if((c == 0) || (c == '\n')) {
220 mountpassword[i] = c;
223 if((i == 64) && (verboseflag)) {
224 printf("\nWarning: password longer than 64 characters specified in cifs password file");
227 if(filename != NULL) {
228 close(file_descript);
234 static int parse_options(char * options, int * filesys_flags)
237 char * percent_char = 0;
239 char * next_keyword = 0;
248 printf("\n parsing options: %s", options);
250 /* while ((data = strsep(&options, ",")) != NULL) { */
251 while(data != NULL) {
252 /* check if ends with trailing comma */
256 /* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
257 /* data = next keyword */
258 /* value = next value ie stuff after equal sign */
260 next_keyword = strchr(data,',');
262 /* temporarily null terminate end of keyword=value pair */
269 /* temporarily null terminate keyword to make keyword and value distinct */
270 if ((value = strchr(data, '=')) != NULL) {
275 if (strncmp(data, "user", 4) == 0) {
276 if (!value || !*value) {
277 printf("invalid or missing username\n");
278 return 1; /* needs_arg; */
280 if (strnlen(value, 260) < 260) {
282 percent_char = strchr(value,'%');
285 if(mountpassword == NULL)
286 mountpassword = calloc(65,1);
289 printf("\ncifs.mount warning - password specified twice\n");
292 strncpy(mountpassword, percent_char,64);
293 /* remove password from username */
294 while(*percent_char != 0) {
301 printf("username too long\n");
304 } else if (strncmp(data, "pass", 4) == 0) {
305 if (!value || !*value) {
307 printf("\npassword specified twice, ignoring second\n");
310 } else if (strnlen(value, 17) < 17) {
312 printf("\ncifs.mount warning - password specified twice\n");
315 printf("password too long\n");
318 } else if (strncmp(data, "ip", 2) == 0) {
319 if (!value || !*value) {
320 printf("target ip address argument missing");
321 } else if (strnlen(value, 35) < 35) {
324 printf("ip address too long\n");
327 } else if ((strncmp(data, "unc", 3) == 0)
328 || (strncmp(data, "target", 6) == 0)
329 || (strncmp(data, "path", 4) == 0)) {
330 if (!value || !*value) {
331 printf("invalid path to network resource\n");
332 return 1; /* needs_arg; */
333 } else if(strnlen(value,5) < 5) {
334 printf("UNC name too short");
337 if (strnlen(value, 300) < 300) {
339 if (strncmp(value, "//", 2) == 0) {
341 printf("unc name specified twice, ignoring second\n");
344 } else if (strncmp(value, "\\\\", 2) != 0) {
345 printf("UNC Path does not begin with // or \\\\ \n");
349 printf("unc name specified twice, ignoring second\n");
354 printf("CIFS: UNC name too long\n");
357 } else if ((strncmp(data, "domain", 3) == 0)
358 || (strncmp(data, "workgroup", 5) == 0)) {
359 if (!value || !*value) {
360 printf("CIFS: invalid domain name\n");
361 return 1; /* needs_arg; */
363 if (strnlen(value, 65) < 65) {
366 printf("domain name too long\n");
369 } else if (strncmp(data, "cred", 4) == 0) {
370 if (value && *value) {
371 rc = open_cred_file(value);
373 printf("error %d opening credential file %s",rc, value);
377 printf("invalid credential file name specified\n");
380 } else if (strncmp(data, "uid", 3) == 0) {
381 if (value && *value) {
384 } else if (strncmp(data, "gid", 3) == 0) {
385 if (value && *value) {
388 /* fmask and dmask synonyms for people used to smbfs syntax */
389 } else if (strcmp(data, "file_mode") == 0 || strcmp(data, "fmask")==0) {
390 if (!value || !*value) {
391 printf ("Option '%s' requires a numerical argument\n", data);
395 if (value[0] != '0') {
396 printf ("WARNING: '%s' not expressed in octal.\n", data);
399 if (strcmp (data, "fmask") == 0) {
400 printf ("WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
401 data = "file_mode"; /* BB fix this */
403 } else if (strcmp(data, "dir_mode") == 0 || strcmp(data, "dmask")==0) {
404 if (!value || !*value) {
405 printf ("Option '%s' requires a numerical argument\n", data);
409 if (value[0] != '0') {
410 printf ("WARNING: '%s' not expressed in octal.\n", data);
413 if (strcmp (data, "dmask") == 0) {
414 printf ("WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
417 /* the following eight mount options should be
418 stripped out from what is passed into the kernel
419 since these eight options are best passed as the
420 mount flags rather than redundantly to the kernel
421 and could generate spurious warnings depending on the
422 level of the corresponding cifs vfs kernel code */
423 } else if (strncmp(data, "nosuid", 6) == 0) {
424 *filesys_flags |= MS_NOSUID;
425 } else if (strncmp(data, "suid", 4) == 0) {
426 *filesys_flags &= ~MS_NOSUID;
427 } else if (strncmp(data, "nodev", 5) == 0) {
428 *filesys_flags |= MS_NODEV;
429 } else if (strncmp(data, "dev", 3) == 0) {
430 *filesys_flags &= ~MS_NODEV;
431 } else if (strncmp(data, "noexec", 6) == 0) {
432 *filesys_flags |= MS_NOEXEC;
433 } else if (strncmp(data, "exec", 4) == 0) {
434 *filesys_flags &= ~MS_NOEXEC;
435 } else if (strncmp(data, "ro", 3) == 0) {
436 *filesys_flags |= MS_RDONLY;
437 } else if (strncmp(data, "rw", 2) == 0) {
438 *filesys_flags &= ~MS_RDONLY;
439 } /* else if (strnicmp(data, "port", 4) == 0) {
440 if (value && *value) {
442 simple_strtoul(value, &value, 0);
444 } else if (strnicmp(data, "rsize", 5) == 0) {
445 if (value && *value) {
447 simple_strtoul(value, &value, 0);
449 } else if (strnicmp(data, "wsize", 5) == 0) {
450 if (value && *value) {
452 simple_strtoul(value, &value, 0);
454 } else if (strnicmp(data, "version", 3) == 0) {
456 } else if (strnicmp(data, "rw", 2) == 0) {
459 printf("CIFS: Unknown mount option %s\n",data); */
461 /* move to next option */
462 data = next_keyword+1;
464 /* put overwritten equals sign back */
470 /* put previous overwritten comma back */
480 /* Note that caller frees the returned buffer if necessary */
481 char * parse_server(char * unc_name)
483 int length = strnlen(unc_name,1024);
485 char * ipaddress_string = NULL;
486 struct hostent * host_entry;
487 struct in_addr server_ipaddr;
491 printf("mount error: UNC name too long");
494 if (strncasecmp("cifs://",unc_name,7) == 0)
495 return parse_cifs_url(unc_name+7);
496 if (strncasecmp("smb://",unc_name,6) == 0) {
497 return parse_cifs_url(unc_name+6);
501 /* BB add code to find DFS root here */
502 printf("\nMounting the DFS root for domain not implemented yet");
505 if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
506 printf("mount error: improperly formatted UNC name.");
507 printf(" %s does not begin with \\\\ or //\n",unc_name);
514 if ((share = strchr(unc_name, '/')) ||
515 (share = strchr(unc_name,'\\'))) {
516 *share = 0; /* temporarily terminate the string */
518 host_entry = gethostbyname(unc_name);
519 *(share - 1) = '/'; /* put the slash back */
520 /* rc = getipnodebyname(unc_name, AF_INET, AT_ADDRCONFIG ,&rc);*/
521 if(host_entry == NULL) {
522 printf("mount error: could not find target server. TCP name %s not found ", unc_name);
523 printf(" rc = %d\n",rc);
527 /* BB should we pass an alternate version of the share name as Unicode */
528 /* BB what about ipv6? BB */
529 /* BB add retries with alternate servers in list */
531 memcpy(&server_ipaddr.s_addr, host_entry->h_addr, 4);
533 ipaddress_string = inet_ntoa(server_ipaddr);
534 if(ipaddress_string == NULL) {
535 printf("mount error: could not get valid ip address for target server\n");
538 return ipaddress_string;
541 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
542 printf("Mounting the DFS root for a particular server not implemented yet\n");
549 static struct option longopts[] = {
550 { "all", 0, 0, 'a' },
551 { "help",0, 0, 'h' },
552 { "move",0, 0, 'm' },
553 { "bind",0, 0, 'b' },
554 { "read-only", 0, 0, 'r' },
556 { "verbose", 0, 0, 'v' },
557 { "version", 0, 0, 'V' },
558 { "read-write", 0, 0, 'w' },
560 { "options", 1, 0, 'o' },
561 { "type", 1, 0, 't' },
562 { "rsize",1, 0, 'R' },
563 { "wsize",1, 0, 'W' },
567 { "username",1,0,'u'},
570 { "password",1,0,'p'},
572 { "credentials",1,0,'c'},
574 /* { "uuid",1,0,'U'}, */ /* BB unimplemented */
578 int main(int argc, char ** argv)
581 int flags = MS_MANDLOCK; /* no need to set legacy MS_MGC_VAL */
582 char * orgoptions = NULL;
583 char * share_name = NULL;
584 char * domain_name = NULL;
585 char * ipaddr = NULL;
599 struct utsname sysinfo;
600 struct mntent mountent;
603 /* setlocale(LC_ALL, "");
604 bindtextdomain(PACKAGE, LOCALEDIR);
605 textdomain(PACKAGE); */
608 thisprogram = argv[0];
610 if(thisprogram == NULL)
611 thisprogram = "mount.cifs";
614 /* BB add workstation name and domain and pass down */
616 /* #ifdef _GNU_SOURCE
617 printf(" node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
620 share_name = argv[1];
621 mountpoint = argv[2];
623 /* add sharename in opts string as unc= parm */
625 while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsU:vVwt:",
626 longopts, NULL)) != -1) {
628 /* No code to do the following options yet */
630 list_with_volumelabel = 1;
633 volumelabel = optarg;
653 orgoptions = strdup(optarg);
655 case 'r': /* mount readonly */
665 printf ("mount.cifs version: %s.%s%s\n",
666 MOUNT_CIFS_VERSION_MAJOR,
667 MOUNT_CIFS_VERSION_MINOR,
668 MOUNT_CIFS_VENDOR_SUFFIX);
670 memset(mountpassword,0,64);
677 rsize = atoi(optarg) ;
680 wsize = atoi(optarg);
693 domain_name = optarg;
696 if(mountpassword == NULL)
697 mountpassword = calloc(65,1);
700 strncpy(mountpassword,optarg,64);
706 printf("unknown mount option %c\n",c);
715 if (getenv("PASSWD")) {
716 if(mountpassword == NULL)
717 mountpassword = calloc(65,1);
719 strncpy(mountpassword,getenv("PASSWD"),64);
722 } else if (getenv("PASSWD_FD")) {
723 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
724 } else if (getenv("PASSWD_FILE")) {
725 get_password_from_file(0, getenv("PASSWD_FILE"));
728 ipaddr = parse_server(share_name);
730 if (orgoptions && parse_options(orgoptions, &flags))
733 /* BB save off path and pop after mount returns? */
734 /* BB canonicalize the path in argv[1]? */
736 if(chdir(mountpoint)) {
737 printf("mount error: can not change directory into mount target %s\n",mountpoint);
740 if(stat (mountpoint, &statbuf)) {
741 printf("mount error: mount point %s does not exist\n",mountpoint);
745 if (S_ISDIR(statbuf.st_mode) == 0) {
746 printf("mount error: mount point %s is not a directory\n",mountpoint);
750 if((getuid() != 0) && (geteuid() == 0)) {
751 if((statbuf.st_uid == getuid()) && (S_IRWXU == (statbuf.st_mode & S_IRWXU))) {
752 printf("setuid mount allowed\n");
754 printf("mount error: permission denied or not superuser and cifs.mount not installed SUID\n");
760 user_name = getusername();
762 if(got_password == 0) {
763 mountpassword = getpass("Password: "); /* BB obsolete */
766 /* FIXME launch daemon (handles dfs name resolution and credential change)
767 remember to clear parms and overwrite password field before launching */
769 optlen = strlen(orgoptions);
774 optlen += strlen(share_name) + 4;
776 optlen += strlen(user_name) + 6;
778 optlen += strlen(ipaddr) + 4;
780 optlen += strlen(mountpassword) + 6;
781 options = malloc(optlen + 10);
784 strncat(options,"unc=",4);
785 strcat(options,share_name);
786 /* scan backwards and reverse direction of slash */
787 temp = strrchr(options, '/');
788 if(temp > options + 6)
791 strncat(options,",ip=",4);
792 strcat(options,ipaddr);
795 strncat(options,",user=",6);
796 strcat(options,user_name);
799 strncat(options,",pass=",6);
800 strcat(options,mountpassword);
802 strncat(options,",ver=",5);
803 strcat(options,MOUNT_CIFS_VERSION_MAJOR);
807 strcat(options,orgoptions);
810 printf("\ncifs.mount kernel mount options %s \n",options);
811 if(mount(share_name, mountpoint, "cifs", flags, options)) {
812 /* remember to kill daemon on error */
815 printf("mount failed but no error number set\n");
818 printf("mount error: cifs filesystem not supported by the system\n");
821 printf("mount error %d = %s\n",errno,strerror(errno));
823 printf("Refer to the mount.cifs(8) manual page (e.g.man mount.cifs)\n");
825 memset(mountpassword,0,64);
829 pmntfile = setmntent(MOUNTED, "a+");
831 mountent.mnt_fsname = share_name;
832 mountent.mnt_dir = mountpoint;
833 mountent.mnt_type = "cifs";
834 mountent.mnt_opts = "";
835 mountent.mnt_freq = 0;
836 mountent.mnt_passno = 0;
837 rc = addmntent(pmntfile,&mountent);
840 printf("could not update mount table\n");
844 memset(mountpassword,0,64);
849 memset(options,0,optlen);
854 memset(orgoptions,0,orgoptlen);