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 "4"
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 int free_share_name = 0;
61 static char * user_name = NULL;
62 char * mountpassword = NULL;
68 open nofollow - avoid symlink exposure?
69 get owner of dir see if matches self or if root
70 call system(umount argv) etc.
74 static void mount_cifs_usage(void)
76 printf("\nUsage: %s <remotetarget> <dir> -o <options>\n", thisprogram);
77 printf("\nMount the remote target, specified as a UNC name,");
78 printf(" to a local directory.\n\nOptions:\n");
79 printf("\tuser=<arg>\n\tpass=<arg>\n\tdom=<arg>\n");
80 printf("\nOther less commonly used options are described in the manual page");
81 printf("\n\tman 8 mount.cifs\n");
82 printf("\nTo display the version number of the mount helper:");
83 printf("\n\t%s -V\n",thisprogram);
86 memset(mountpassword,0,64);
92 /* caller frees username if necessary */
93 static char * getusername(void) {
94 char *username = NULL;
95 struct passwd *password = getpwuid(getuid());
98 username = password->pw_name;
103 char * parse_cifs_url(char * unc_name)
105 printf("\nMounting cifs URL not implemented yet. Attempt to mount %s\n",unc_name);
109 static int open_cred_file(char * file_name)
115 fs = fopen(file_name,"r");
118 line_buf = malloc(4096);
122 while(fgets(line_buf,4096,fs)) {
123 /* parse line from credential file */
125 /* eat leading white space */
126 for(i=0;i<4086;i++) {
127 if((line_buf[i] != ' ') && (line_buf[i] != '\t'))
129 /* if whitespace - skip past it */
131 if (strncasecmp("username",line_buf+i,8) == 0) {
132 temp_val = strchr(line_buf + i,'=');
134 /* go past equals sign */
136 for(length = 0;length<4087;length++) {
137 if(temp_val[length] == '\n')
141 printf("mount.cifs failed due to malformed username in credentials file");
142 memset(line_buf,0,4096);
144 memset(mountpassword,0,64);
149 user_name = calloc(1 + length,1);
150 /* BB adding free of user_name string before exit,
151 not really necessary but would be cleaner */
152 strncpy(user_name,temp_val, length);
155 } else if (strncasecmp("password",line_buf+i,8) == 0) {
156 temp_val = strchr(line_buf+i,'=');
158 /* go past equals sign */
160 for(length = 0;length<65;length++) {
161 if(temp_val[length] == '\n')
165 printf("mount.cifs failed: password in credentials file too long\n");
166 memset(line_buf,0, 4096);
168 memset(mountpassword,0,64);
172 if(mountpassword == NULL) {
173 mountpassword = calloc(65,1);
175 memset(mountpassword,0,64);
177 /* BB add handling for commas in password here */
178 strncpy(mountpassword,temp_val,length);
187 memset(line_buf,0,4096);
193 static int get_password_from_file(int file_descript, char * filename)
199 if(mountpassword == NULL)
200 mountpassword = calloc(65,1);
202 memset(mountpassword, 0, 64);
204 if(filename != NULL) {
205 file_descript = open(filename, O_RDONLY);
206 if(file_descript < 0) {
207 printf("mount.cifs failed. %s attempting to open password file %s\n",
208 strerror(errno),filename);
212 /* else file already open and fd provided */
215 rc = read(file_descript,&c,1);
217 printf("mount.cifs failed. Error %s reading password file\n",strerror(errno));
218 memset(mountpassword,0,64);
220 close(file_descript);
223 if(mountpassword[0] == 0) {
225 printf("\nWarning: null password used since cifs password file empty");
228 } else /* read valid character */ {
229 if((c == 0) || (c == '\n')) {
232 mountpassword[i] = c;
235 if((i == 64) && (verboseflag)) {
236 printf("\nWarning: password longer than 64 characters specified in cifs password file");
239 if(filename != NULL) {
240 close(file_descript);
246 static int parse_options(char * options, int * filesys_flags)
249 char * percent_char = 0;
251 char * next_keyword = 0;
260 printf("\n parsing options: %s", options);
262 /* while ((data = strsep(&options, ",")) != NULL) { */
263 while(data != NULL) {
264 /* check if ends with trailing comma */
268 /* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
269 /* data = next keyword */
270 /* value = next value ie stuff after equal sign */
272 next_keyword = strchr(data,',');
274 /* temporarily null terminate end of keyword=value pair */
281 /* temporarily null terminate keyword to make keyword and value distinct */
282 if ((value = strchr(data, '=')) != NULL) {
287 if (strncmp(data, "user", 4) == 0) {
288 if (!value || !*value) {
289 if(data[4] == '\0') {
291 printf("\nskipping empty user mount parameter\n");
292 /* remove the parm since it would otherwise be confusing
293 to the kernel code which would think it was a real username */
298 /* BB remove it from mount line so as not to confuse kernel code */
300 printf("username specified with no parameter\n");
301 return 1; /* needs_arg; */
304 if (strnlen(value, 260) < 260) {
306 percent_char = strchr(value,'%');
309 if(mountpassword == NULL)
310 mountpassword = calloc(65,1);
313 printf("\nmount.cifs warning - password specified twice\n");
316 strncpy(mountpassword, percent_char,64);
317 /* remove password from username */
318 while(*percent_char != 0) {
325 printf("username too long\n");
329 } else if (strncmp(data, "pass", 4) == 0) {
330 if (!value || !*value) {
332 printf("\npassword specified twice, ignoring second\n");
335 } else if (strnlen(value, 17) < 17) {
337 printf("\nmount.cifs warning - password specified twice\n");
340 printf("password too long\n");
343 } else if (strncmp(data, "ip", 2) == 0) {
344 if (!value || !*value) {
345 printf("target ip address argument missing");
346 } else if (strnlen(value, 35) < 35) {
349 printf("ip address too long\n");
352 } else if ((strncmp(data, "unc", 3) == 0)
353 || (strncmp(data, "target", 6) == 0)
354 || (strncmp(data, "path", 4) == 0)) {
355 if (!value || !*value) {
356 printf("invalid path to network resource\n");
357 return 1; /* needs_arg; */
358 } else if(strnlen(value,5) < 5) {
359 printf("UNC name too short");
362 if (strnlen(value, 300) < 300) {
364 if (strncmp(value, "//", 2) == 0) {
366 printf("unc name specified twice, ignoring second\n");
369 } else if (strncmp(value, "\\\\", 2) != 0) {
370 printf("UNC Path does not begin with // or \\\\ \n");
374 printf("unc name specified twice, ignoring second\n");
379 printf("CIFS: UNC name too long\n");
382 } else if ((strncmp(data, "domain", 3) == 0)
383 || (strncmp(data, "workgroup", 5) == 0)) {
384 if (!value || !*value) {
385 printf("CIFS: invalid domain name\n");
386 return 1; /* needs_arg; */
388 if (strnlen(value, 65) < 65) {
391 printf("domain name too long\n");
394 } else if (strncmp(data, "cred", 4) == 0) {
395 if (value && *value) {
396 rc = open_cred_file(value);
398 printf("error %d opening credential file %s\n",rc, value);
402 printf("invalid credential file name specified\n");
405 } else if (strncmp(data, "uid", 3) == 0) {
406 if (value && *value) {
409 } else if (strncmp(data, "gid", 3) == 0) {
410 if (value && *value) {
413 /* fmask and dmask synonyms for people used to smbfs syntax */
414 } else if (strcmp(data, "file_mode") == 0 || strcmp(data, "fmask")==0) {
415 if (!value || !*value) {
416 printf ("Option '%s' requires a numerical argument\n", data);
420 if (value[0] != '0') {
421 printf ("WARNING: '%s' not expressed in octal.\n", data);
424 if (strcmp (data, "fmask") == 0) {
425 printf ("WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
426 data = "file_mode"; /* BB fix this */
428 } else if (strcmp(data, "dir_mode") == 0 || strcmp(data, "dmask")==0) {
429 if (!value || !*value) {
430 printf ("Option '%s' requires a numerical argument\n", data);
434 if (value[0] != '0') {
435 printf ("WARNING: '%s' not expressed in octal.\n", data);
438 if (strcmp (data, "dmask") == 0) {
439 printf ("WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
442 /* the following eight mount options should be
443 stripped out from what is passed into the kernel
444 since these eight options are best passed as the
445 mount flags rather than redundantly to the kernel
446 and could generate spurious warnings depending on the
447 level of the corresponding cifs vfs kernel code */
448 } else if (strncmp(data, "nosuid", 6) == 0) {
449 *filesys_flags |= MS_NOSUID;
450 } else if (strncmp(data, "suid", 4) == 0) {
451 *filesys_flags &= ~MS_NOSUID;
452 } else if (strncmp(data, "nodev", 5) == 0) {
453 *filesys_flags |= MS_NODEV;
454 } else if (strncmp(data, "dev", 3) == 0) {
455 *filesys_flags &= ~MS_NODEV;
456 } else if (strncmp(data, "noexec", 6) == 0) {
457 *filesys_flags |= MS_NOEXEC;
458 } else if (strncmp(data, "exec", 4) == 0) {
459 *filesys_flags &= ~MS_NOEXEC;
460 } else if (strncmp(data, "guest", 5) == 0) {
462 } else if (strncmp(data, "ro", 2) == 0) {
463 *filesys_flags |= MS_RDONLY;
464 } else if (strncmp(data, "rw", 2) == 0) {
465 *filesys_flags &= ~MS_RDONLY;
466 } /* else if (strnicmp(data, "port", 4) == 0) {
467 if (value && *value) {
469 simple_strtoul(value, &value, 0);
471 } else if (strnicmp(data, "rsize", 5) == 0) {
472 if (value && *value) {
474 simple_strtoul(value, &value, 0);
476 } else if (strnicmp(data, "wsize", 5) == 0) {
477 if (value && *value) {
479 simple_strtoul(value, &value, 0);
481 } else if (strnicmp(data, "version", 3) == 0) {
483 printf("CIFS: Unknown mount option %s\n",data);
484 } */ /* nothing to do on those four mount options above.
485 Just pass to kernel and ignore them here */
487 /* move to next option */
488 data = next_keyword+1;
490 /* put overwritten equals sign back */
496 /* put previous overwritten comma back */
505 /* Note that caller frees the returned buffer if necessary */
506 char * parse_server(char ** punc_name)
508 char * unc_name = *punc_name;
509 int length = strnlen(unc_name,1024);
511 char * ipaddress_string = NULL;
512 struct hostent * host_entry;
513 struct in_addr server_ipaddr;
517 printf("mount error: UNC name too long");
520 if (strncasecmp("cifs://",unc_name,7) == 0)
521 return parse_cifs_url(unc_name+7);
522 if (strncasecmp("smb://",unc_name,6) == 0) {
523 return parse_cifs_url(unc_name+6);
527 /* BB add code to find DFS root here */
528 printf("\nMounting the DFS root for domain not implemented yet");
531 if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
532 /* check for nfs syntax ie server:share */
533 share = strchr(unc_name,':');
536 *punc_name = malloc(length+3);
538 strncpy((*punc_name)+2,unc_name,length);
539 unc_name = *punc_name;
540 unc_name[length+2] = 0;
541 goto continue_unc_parsing;
543 printf("mount error: improperly formatted UNC name.");
544 printf(" %s does not begin with \\\\ or //\n",unc_name);
548 continue_unc_parsing:
552 if ((share = strchr(unc_name, '/')) ||
553 (share = strchr(unc_name,'\\'))) {
554 *share = 0; /* temporarily terminate the string */
556 host_entry = gethostbyname(unc_name);
557 *(share - 1) = '/'; /* put the slash back */
558 /* rc = getipnodebyname(unc_name, AF_INET, AT_ADDRCONFIG ,&rc);*/
559 if(host_entry == NULL) {
560 printf("mount error: could not find target server. TCP name %s not found ", unc_name);
561 printf(" rc = %d\n",rc);
565 /* BB should we pass an alternate version of the share name as Unicode */
566 /* BB what about ipv6? BB */
567 /* BB add retries with alternate servers in list */
569 memcpy(&server_ipaddr.s_addr, host_entry->h_addr, 4);
571 ipaddress_string = inet_ntoa(server_ipaddr);
572 if(ipaddress_string == NULL) {
573 printf("mount error: could not get valid ip address for target server\n");
576 return ipaddress_string;
579 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
580 printf("Mounting the DFS root for a particular server not implemented yet\n");
587 static struct option longopts[] = {
588 { "all", 0, 0, 'a' },
589 { "help",0, 0, 'h' },
590 { "move",0, 0, 'm' },
591 { "bind",0, 0, 'b' },
592 { "read-only", 0, 0, 'r' },
594 { "verbose", 0, 0, 'v' },
595 { "version", 0, 0, 'V' },
596 { "read-write", 0, 0, 'w' },
598 { "options", 1, 0, 'o' },
599 { "type", 1, 0, 't' },
600 { "rsize",1, 0, 'R' },
601 { "wsize",1, 0, 'W' },
605 { "username",1,0,'u'},
608 { "password",1,0,'p'},
610 { "credentials",1,0,'c'},
612 /* { "uuid",1,0,'U'}, */ /* BB unimplemented */
616 int main(int argc, char ** argv)
619 int flags = MS_MANDLOCK; /* no need to set legacy MS_MGC_VAL */
620 char * orgoptions = NULL;
621 char * share_name = NULL;
622 char * domain_name = NULL;
623 char * ipaddr = NULL;
627 char * resolved_path;
638 struct utsname sysinfo;
639 struct mntent mountent;
642 /* setlocale(LC_ALL, "");
643 bindtextdomain(PACKAGE, LOCALEDIR);
644 textdomain(PACKAGE); */
647 thisprogram = argv[0];
649 if(thisprogram == NULL)
650 thisprogram = "mount.cifs";
653 /* BB add workstation name and domain and pass down */
655 /* #ifdef _GNU_SOURCE
656 printf(" node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
659 share_name = argv[1];
660 mountpoint = argv[2];
662 /* add sharename in opts string as unc= parm */
664 while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsSU:vVwt:",
665 longopts, NULL)) != -1) {
667 /* No code to do the following options yet */
669 list_with_volumelabel = 1;
672 volumelabel = optarg;
692 orgoptions = strdup(optarg);
694 case 'r': /* mount readonly */
704 printf ("mount.cifs version: %s.%s%s\n",
705 MOUNT_CIFS_VERSION_MAJOR,
706 MOUNT_CIFS_VERSION_MINOR,
707 MOUNT_CIFS_VENDOR_SUFFIX);
709 memset(mountpassword,0,64);
716 rsize = atoi(optarg) ;
719 wsize = atoi(optarg);
732 domain_name = optarg;
735 if(mountpassword == NULL)
736 mountpassword = calloc(65,1);
739 strncpy(mountpassword,optarg,64);
743 get_password_from_file(0 /* stdin */,NULL);
748 printf("unknown mount option %c\n",c);
757 if (getenv("PASSWD")) {
758 if(mountpassword == NULL)
759 mountpassword = calloc(65,1);
761 strncpy(mountpassword,getenv("PASSWD"),64);
764 } else if (getenv("PASSWD_FD")) {
765 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
766 } else if (getenv("PASSWD_FILE")) {
767 get_password_from_file(0, getenv("PASSWD_FILE"));
770 ipaddr = parse_server(&share_name);
774 if (orgoptions && parse_options(orgoptions, &flags))
777 /* BB save off path and pop after mount returns? */
778 resolved_path = malloc(PATH_MAX+1);
780 /* Note that if we can not canonicalize the name, we get
781 another chance to see if it is valid when we chdir to it */
782 if (realpath(mountpoint, resolved_path)) {
783 mountpoint = resolved_path;
786 if(chdir(mountpoint)) {
787 printf("mount error: can not change directory into mount target %s\n",mountpoint);
791 if(stat (".", &statbuf)) {
792 printf("mount error: mount point %s does not exist\n",mountpoint);
796 if (S_ISDIR(statbuf.st_mode) == 0) {
797 printf("mount error: mount point %s is not a directory\n",mountpoint);
801 if((getuid() != 0) && (geteuid() == 0)) {
802 if((statbuf.st_uid == getuid()) && (S_IRWXU == (statbuf.st_mode & S_IRWXU))) {
803 #ifndef CIFS_ALLOW_USR_SUID
804 /* Do not allow user mounts to control suid flag
805 for mount unless explicitly built that way */
806 flags |= MS_NOSUID | MS_NODEV;
809 printf("mount error: permission denied or not superuser and mount.cifs not installed SUID\n");
815 user_name = getusername();
817 if(got_password == 0) {
818 mountpassword = getpass("Password: "); /* BB obsolete */
821 /* FIXME launch daemon (handles dfs name resolution and credential change)
822 remember to clear parms and overwrite password field before launching */
824 optlen = strlen(orgoptions);
829 optlen += strlen(share_name) + 4;
831 optlen += strlen(user_name) + 6;
833 optlen += strlen(ipaddr) + 4;
835 optlen += strlen(mountpassword) + 6;
836 options = malloc(optlen + 10);
838 if(options == NULL) {
839 printf("Could not allocate memory for mount options\n");
845 strncat(options,"unc=",4);
846 strcat(options,share_name);
847 /* scan backwards and reverse direction of slash */
848 temp = strrchr(options, '/');
849 if(temp > options + 6)
852 strncat(options,",ip=",4);
853 strcat(options,ipaddr);
856 strncat(options,",user=",6);
857 strcat(options,user_name);
860 strncat(options,",pass=",6);
861 strcat(options,mountpassword);
863 strncat(options,",ver=",5);
864 strcat(options,MOUNT_CIFS_VERSION_MAJOR);
868 strcat(options,orgoptions);
871 printf("\nmount.cifs kernel mount options %s \n",options);
872 if(mount(share_name, mountpoint, "cifs", flags, options)) {
873 /* remember to kill daemon on error */
876 printf("mount failed but no error number set\n");
879 printf("mount error: cifs filesystem not supported by the system\n");
882 printf("mount error %d = %s\n",errno,strerror(errno));
884 printf("Refer to the mount.cifs(8) manual page (e.g.man mount.cifs)\n");
886 memset(mountpassword,0,64);
890 pmntfile = setmntent(MOUNTED, "a+");
892 mountent.mnt_fsname = share_name;
893 mountent.mnt_dir = mountpoint;
894 mountent.mnt_type = "cifs";
895 mountent.mnt_opts = malloc(220);
896 if(mountent.mnt_opts) {
897 char * mount_user = getusername();
898 memset(mountent.mnt_opts,0,200);
899 if(flags & MS_RDONLY)
900 strcat(mountent.mnt_opts,"ro");
902 strcat(mountent.mnt_opts,"rw");
903 if(flags & MS_MANDLOCK)
904 strcat(mountent.mnt_opts,",mand");
906 strcat(mountent.mnt_opts,",nomand");
907 if(flags & MS_NOEXEC)
908 strcat(mountent.mnt_opts,",noexec");
909 if(flags & MS_NOSUID)
910 strcat(mountent.mnt_opts,",nosuid");
912 strcat(mountent.mnt_opts,",nodev");
913 if(flags & MS_SYNCHRONOUS)
914 strcat(mountent.mnt_opts,",synch");
917 strcat(mountent.mnt_opts,",user=");
918 strcat(mountent.mnt_opts,mount_user);
923 mountent.mnt_freq = 0;
924 mountent.mnt_passno = 0;
925 rc = addmntent(pmntfile,&mountent);
927 if(mountent.mnt_opts)
928 free(mountent.mnt_opts);
930 printf("could not update mount table\n");
934 memset(mountpassword,0,64);
939 memset(options,0,optlen);
944 memset(orgoptions,0,orgoptlen);
951 if(free_share_name) {