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", 2) == 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 printf("CIFS: Unknown mount option %s\n",data);
457 } */ /* nothing to do on those four mount options above.
458 Just pass to kernel and ignore them here */
460 /* move to next option */
461 data = next_keyword+1;
463 /* put overwritten equals sign back */
469 /* put previous overwritten comma back */
479 /* Note that caller frees the returned buffer if necessary */
480 char * parse_server(char * unc_name)
482 int length = strnlen(unc_name,1024);
484 char * ipaddress_string = NULL;
485 struct hostent * host_entry;
486 struct in_addr server_ipaddr;
490 printf("mount error: UNC name too long");
493 if (strncasecmp("cifs://",unc_name,7) == 0)
494 return parse_cifs_url(unc_name+7);
495 if (strncasecmp("smb://",unc_name,6) == 0) {
496 return parse_cifs_url(unc_name+6);
500 /* BB add code to find DFS root here */
501 printf("\nMounting the DFS root for domain not implemented yet");
504 if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
505 printf("mount error: improperly formatted UNC name.");
506 printf(" %s does not begin with \\\\ or //\n",unc_name);
513 if ((share = strchr(unc_name, '/')) ||
514 (share = strchr(unc_name,'\\'))) {
515 *share = 0; /* temporarily terminate the string */
517 host_entry = gethostbyname(unc_name);
518 *(share - 1) = '/'; /* put the slash back */
519 /* rc = getipnodebyname(unc_name, AF_INET, AT_ADDRCONFIG ,&rc);*/
520 if(host_entry == NULL) {
521 printf("mount error: could not find target server. TCP name %s not found ", unc_name);
522 printf(" rc = %d\n",rc);
526 /* BB should we pass an alternate version of the share name as Unicode */
527 /* BB what about ipv6? BB */
528 /* BB add retries with alternate servers in list */
530 memcpy(&server_ipaddr.s_addr, host_entry->h_addr, 4);
532 ipaddress_string = inet_ntoa(server_ipaddr);
533 if(ipaddress_string == NULL) {
534 printf("mount error: could not get valid ip address for target server\n");
537 return ipaddress_string;
540 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
541 printf("Mounting the DFS root for a particular server not implemented yet\n");
548 static struct option longopts[] = {
549 { "all", 0, 0, 'a' },
550 { "help",0, 0, 'h' },
551 { "move",0, 0, 'm' },
552 { "bind",0, 0, 'b' },
553 { "read-only", 0, 0, 'r' },
555 { "verbose", 0, 0, 'v' },
556 { "version", 0, 0, 'V' },
557 { "read-write", 0, 0, 'w' },
559 { "options", 1, 0, 'o' },
560 { "type", 1, 0, 't' },
561 { "rsize",1, 0, 'R' },
562 { "wsize",1, 0, 'W' },
566 { "username",1,0,'u'},
569 { "password",1,0,'p'},
571 { "credentials",1,0,'c'},
573 /* { "uuid",1,0,'U'}, */ /* BB unimplemented */
577 int main(int argc, char ** argv)
580 int flags = MS_MANDLOCK; /* no need to set legacy MS_MGC_VAL */
581 char * orgoptions = NULL;
582 char * share_name = NULL;
583 char * domain_name = NULL;
584 char * ipaddr = NULL;
598 struct utsname sysinfo;
599 struct mntent mountent;
602 /* setlocale(LC_ALL, "");
603 bindtextdomain(PACKAGE, LOCALEDIR);
604 textdomain(PACKAGE); */
607 thisprogram = argv[0];
609 if(thisprogram == NULL)
610 thisprogram = "mount.cifs";
613 /* BB add workstation name and domain and pass down */
615 /* #ifdef _GNU_SOURCE
616 printf(" node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
619 share_name = argv[1];
620 mountpoint = argv[2];
622 /* add sharename in opts string as unc= parm */
624 while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsU:vVwt:",
625 longopts, NULL)) != -1) {
627 /* No code to do the following options yet */
629 list_with_volumelabel = 1;
632 volumelabel = optarg;
652 orgoptions = strdup(optarg);
654 case 'r': /* mount readonly */
664 printf ("mount.cifs version: %s.%s%s\n",
665 MOUNT_CIFS_VERSION_MAJOR,
666 MOUNT_CIFS_VERSION_MINOR,
667 MOUNT_CIFS_VENDOR_SUFFIX);
669 memset(mountpassword,0,64);
676 rsize = atoi(optarg) ;
679 wsize = atoi(optarg);
692 domain_name = optarg;
695 if(mountpassword == NULL)
696 mountpassword = calloc(65,1);
699 strncpy(mountpassword,optarg,64);
705 printf("unknown mount option %c\n",c);
714 if (getenv("PASSWD")) {
715 if(mountpassword == NULL)
716 mountpassword = calloc(65,1);
718 strncpy(mountpassword,getenv("PASSWD"),64);
721 } else if (getenv("PASSWD_FD")) {
722 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
723 } else if (getenv("PASSWD_FILE")) {
724 get_password_from_file(0, getenv("PASSWD_FILE"));
727 ipaddr = parse_server(share_name);
729 if (orgoptions && parse_options(orgoptions, &flags))
732 /* BB save off path and pop after mount returns? */
733 /* BB canonicalize the path in argv[1]? */
735 if(chdir(mountpoint)) {
736 printf("mount error: can not change directory into mount target %s\n",mountpoint);
739 if(stat (mountpoint, &statbuf)) {
740 printf("mount error: mount point %s does not exist\n",mountpoint);
744 if (S_ISDIR(statbuf.st_mode) == 0) {
745 printf("mount error: mount point %s is not a directory\n",mountpoint);
749 if((getuid() != 0) && (geteuid() == 0)) {
750 if((statbuf.st_uid == getuid()) && (S_IRWXU == (statbuf.st_mode & S_IRWXU))) {
751 #ifndef CIFS_ALLOW_USR_SUID
752 /* Do not allow user mounts to control suid flag
753 for mount unless explicitly built that way */
754 flags |= MS_NOSUID | MS_NODEV;
757 printf("mount error: permission denied or not superuser and cifs.mount not installed SUID\n");
763 user_name = getusername();
765 if(got_password == 0) {
766 mountpassword = getpass("Password: "); /* BB obsolete */
769 /* FIXME launch daemon (handles dfs name resolution and credential change)
770 remember to clear parms and overwrite password field before launching */
772 optlen = strlen(orgoptions);
777 optlen += strlen(share_name) + 4;
779 optlen += strlen(user_name) + 6;
781 optlen += strlen(ipaddr) + 4;
783 optlen += strlen(mountpassword) + 6;
784 options = malloc(optlen + 10);
787 strncat(options,"unc=",4);
788 strcat(options,share_name);
789 /* scan backwards and reverse direction of slash */
790 temp = strrchr(options, '/');
791 if(temp > options + 6)
794 strncat(options,",ip=",4);
795 strcat(options,ipaddr);
798 strncat(options,",user=",6);
799 strcat(options,user_name);
802 strncat(options,",pass=",6);
803 strcat(options,mountpassword);
805 strncat(options,",ver=",5);
806 strcat(options,MOUNT_CIFS_VERSION_MAJOR);
810 strcat(options,orgoptions);
813 printf("\ncifs.mount kernel mount options %s \n",options);
814 if(mount(share_name, mountpoint, "cifs", flags, options)) {
815 /* remember to kill daemon on error */
818 printf("mount failed but no error number set\n");
821 printf("mount error: cifs filesystem not supported by the system\n");
824 printf("mount error %d = %s\n",errno,strerror(errno));
826 printf("Refer to the mount.cifs(8) manual page (e.g.man mount.cifs)\n");
828 memset(mountpassword,0,64);
832 pmntfile = setmntent(MOUNTED, "a+");
834 mountent.mnt_fsname = share_name;
835 mountent.mnt_dir = mountpoint;
836 mountent.mnt_type = "cifs";
837 mountent.mnt_opts = "";
838 mountent.mnt_freq = 0;
839 mountent.mnt_passno = 0;
840 rc = addmntent(pmntfile,&mountent);
843 printf("could not update mount table\n");
847 memset(mountpassword,0,64);
852 memset(options,0,optlen);
857 memset(orgoptions,0,orgoptlen);