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. */
28 #include <sys/types.h>
29 #include <sys/mount.h>
31 #include <sys/utsname.h>
32 #include <sys/socket.h>
33 #include <arpa/inet.h>
41 #define MOUNT_CIFS_VERSION_MAJOR "1"
42 #define MOUNT_CIFS_VERSION_MINOR "6"
44 #ifndef MOUNT_CIFS_VENDOR_SUFFIX
45 #define MOUNT_CIFS_VENDOR_SUFFIX ""
54 static int got_password = 0;
55 static int got_user = 0;
56 static int got_domain = 0;
57 static int got_ip = 0;
58 static int got_unc = 0;
59 static int got_uid = 0;
60 static int got_gid = 0;
61 static int free_share_name = 0;
62 static char * user_name = NULL;
63 char * mountpassword = NULL;
69 open nofollow - avoid symlink exposure?
70 get owner of dir see if matches self or if root
71 call system(umount argv) etc.
75 static void mount_cifs_usage(void)
77 printf("\nUsage: %s <remotetarget> <dir> -o <options>\n", thisprogram);
78 printf("\nMount the remote target, specified as a UNC name,");
79 printf(" to a local directory.\n\nOptions:\n");
80 printf("\tuser=<arg>\n\tpass=<arg>\n\tdom=<arg>\n");
81 printf("\nLess commonly used options:");
82 printf("\n\tcredentials=<filename>,guest,perm,noperm,setuids,nosetuids,\n\trw,ro,sep=<char>,iocharset=<codepage>,suid,nosuid,exec,noexec");
83 printf("\n\nOptions not needed for servers supporting CIFS Unix extensions (e.g. most Samba versions):");
84 printf("\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>");
85 printf("\n\nRarely used options:");
86 printf("\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,dev,nodev");
87 printf("\n\nOptions are described in more detail in the manual page");
88 printf("\n\tman 8 mount.cifs\n");
89 printf("\nTo display the version number of the mount helper:");
90 printf("\n\t%s -V\n",thisprogram);
93 memset(mountpassword,0,64);
99 /* caller frees username if necessary */
100 static char * getusername(void) {
101 char *username = NULL;
102 struct passwd *password = getpwuid(getuid());
105 username = password->pw_name;
110 char * parse_cifs_url(char * unc_name)
112 printf("\nMounting cifs URL not implemented yet. Attempt to mount %s\n",unc_name);
116 static int open_cred_file(char * file_name)
122 fs = fopen(file_name,"r");
125 line_buf = malloc(4096);
129 while(fgets(line_buf,4096,fs)) {
130 /* parse line from credential file */
132 /* eat leading white space */
133 for(i=0;i<4086;i++) {
134 if((line_buf[i] != ' ') && (line_buf[i] != '\t'))
136 /* if whitespace - skip past it */
138 if (strncasecmp("username",line_buf+i,8) == 0) {
139 temp_val = strchr(line_buf + i,'=');
141 /* go past equals sign */
143 for(length = 0;length<4087;length++) {
144 if(temp_val[length] == '\n')
148 printf("mount.cifs failed due to malformed username in credentials file");
149 memset(line_buf,0,4096);
151 memset(mountpassword,0,64);
156 user_name = calloc(1 + length,1);
157 /* BB adding free of user_name string before exit,
158 not really necessary but would be cleaner */
159 strncpy(user_name,temp_val, length);
162 } else if (strncasecmp("password",line_buf+i,8) == 0) {
163 temp_val = strchr(line_buf+i,'=');
165 /* go past equals sign */
167 for(length = 0;length<65;length++) {
168 if(temp_val[length] == '\n')
172 printf("mount.cifs failed: password in credentials file too long\n");
173 memset(line_buf,0, 4096);
175 memset(mountpassword,0,64);
179 if(mountpassword == NULL) {
180 mountpassword = calloc(65,1);
182 memset(mountpassword,0,64);
184 /* BB add handling for commas in password here */
185 strncpy(mountpassword,temp_val,length);
194 memset(line_buf,0,4096);
200 static int get_password_from_file(int file_descript, char * filename)
206 if(mountpassword == NULL)
207 mountpassword = calloc(65,1);
209 memset(mountpassword, 0, 64);
211 if(filename != NULL) {
212 file_descript = open(filename, O_RDONLY);
213 if(file_descript < 0) {
214 printf("mount.cifs failed. %s attempting to open password file %s\n",
215 strerror(errno),filename);
219 /* else file already open and fd provided */
222 rc = read(file_descript,&c,1);
224 printf("mount.cifs failed. Error %s reading password file\n",strerror(errno));
225 memset(mountpassword,0,64);
227 close(file_descript);
230 if(mountpassword[0] == 0) {
232 printf("\nWarning: null password used since cifs password file empty");
235 } else /* read valid character */ {
236 if((c == 0) || (c == '\n')) {
239 mountpassword[i] = c;
242 if((i == 64) && (verboseflag)) {
243 printf("\nWarning: password longer than 64 characters specified in cifs password file");
246 if(filename != NULL) {
247 close(file_descript);
253 static int parse_options(char * options, int * filesys_flags)
256 char * percent_char = NULL;
258 char * next_keyword = NULL;
267 printf("\n parsing options: %s", options);
269 /* while ((data = strsep(&options, ",")) != NULL) { */
270 while(data != NULL) {
271 /* check if ends with trailing comma */
275 /* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
276 /* data = next keyword */
277 /* value = next value ie stuff after equal sign */
279 next_keyword = strchr(data,',');
281 /* temporarily null terminate end of keyword=value pair */
288 /* temporarily null terminate keyword to make keyword and value distinct */
289 if ((value = strchr(data, '=')) != NULL) {
294 if (strncmp(data, "users",5) == 0) {
295 if(!value || !*value) {
296 strncpy(data,",,,,,",5);
298 } else if (strncmp(data, "user", 4) == 0) {
299 if (!value || !*value) {
300 if(data[4] == '\0') {
302 printf("\nskipping empty user mount parameter\n");
303 /* remove the parm since it would otherwise be confusing
304 to the kernel code which would think it was a real username */
309 /* BB remove it from mount line so as not to confuse kernel code */
311 printf("username specified with no parameter\n");
312 return 1; /* needs_arg; */
315 if (strnlen(value, 260) < 260) {
317 percent_char = strchr(value,'%');
320 if(mountpassword == NULL)
321 mountpassword = calloc(65,1);
324 printf("\nmount.cifs warning - password specified twice\n");
327 strncpy(mountpassword, percent_char,64);
328 /* remove password from username */
329 while(*percent_char != 0) {
336 printf("username too long\n");
340 } else if (strncmp(data, "pass", 4) == 0) {
341 if (!value || !*value) {
343 printf("\npassword specified twice, ignoring second\n");
346 } else if (strnlen(value, 17) < 17) {
348 printf("\nmount.cifs warning - password specified twice\n");
351 printf("password too long\n");
354 } else if (strncmp(data, "ip", 2) == 0) {
355 if (!value || !*value) {
356 printf("target ip address argument missing");
357 } else if (strnlen(value, 35) < 35) {
359 printf("ip address %s override specified\n",value);
362 printf("ip address too long\n");
365 } else if ((strncmp(data, "unc", 3) == 0)
366 || (strncmp(data, "target", 6) == 0)
367 || (strncmp(data, "path", 4) == 0)) {
368 if (!value || !*value) {
369 printf("invalid path to network resource\n");
370 return 1; /* needs_arg; */
371 } else if(strnlen(value,5) < 5) {
372 printf("UNC name too short");
375 if (strnlen(value, 300) < 300) {
377 if (strncmp(value, "//", 2) == 0) {
379 printf("unc name specified twice, ignoring second\n");
382 } else if (strncmp(value, "\\\\", 2) != 0) {
383 printf("UNC Path does not begin with // or \\\\ \n");
387 printf("unc name specified twice, ignoring second\n");
392 printf("CIFS: UNC name too long\n");
395 } else if ((strncmp(data, "domain", 3) == 0)
396 || (strncmp(data, "workgroup", 5) == 0)) {
397 if (!value || !*value) {
398 printf("CIFS: invalid domain name\n");
399 return 1; /* needs_arg; */
401 if (strnlen(value, 65) < 65) {
404 printf("domain name too long\n");
407 } else if (strncmp(data, "cred", 4) == 0) {
408 if (value && *value) {
409 rc = open_cred_file(value);
411 printf("error %d opening credential file %s\n",rc, value);
415 printf("invalid credential file name specified\n");
418 } else if (strncmp(data, "uid", 3) == 0) {
419 if (value && *value) {
422 } else if (strncmp(data, "gid", 3) == 0) {
423 if (value && *value) {
426 /* fmask and dmask synonyms for people used to smbfs syntax */
427 } else if (strcmp(data, "file_mode") == 0 || strcmp(data, "fmask")==0) {
428 if (!value || !*value) {
429 printf ("Option '%s' requires a numerical argument\n", data);
433 if (value[0] != '0') {
434 printf ("WARNING: '%s' not expressed in octal.\n", data);
437 if (strcmp (data, "fmask") == 0) {
438 printf ("WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
439 data = "file_mode"; /* BB fix this */
441 } else if (strcmp(data, "dir_mode") == 0 || strcmp(data, "dmask")==0) {
442 if (!value || !*value) {
443 printf ("Option '%s' requires a numerical argument\n", data);
447 if (value[0] != '0') {
448 printf ("WARNING: '%s' not expressed in octal.\n", data);
451 if (strcmp (data, "dmask") == 0) {
452 printf ("WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
455 /* the following eight mount options should be
456 stripped out from what is passed into the kernel
457 since these eight options are best passed as the
458 mount flags rather than redundantly to the kernel
459 and could generate spurious warnings depending on the
460 level of the corresponding cifs vfs kernel code */
461 } else if (strncmp(data, "nosuid", 6) == 0) {
462 *filesys_flags |= MS_NOSUID;
463 } else if (strncmp(data, "suid", 4) == 0) {
464 *filesys_flags &= ~MS_NOSUID;
465 } else if (strncmp(data, "nodev", 5) == 0) {
466 *filesys_flags |= MS_NODEV;
467 } else if (strncmp(data, "dev", 3) == 0) {
468 *filesys_flags &= ~MS_NODEV;
469 } else if (strncmp(data, "noexec", 6) == 0) {
470 *filesys_flags |= MS_NOEXEC;
471 } else if (strncmp(data, "exec", 4) == 0) {
472 *filesys_flags &= ~MS_NOEXEC;
473 } else if (strncmp(data, "guest", 5) == 0) {
475 /* remove the parm since it would otherwise be logged by kern */
481 } else if (strncmp(data, "ro", 2) == 0) {
482 *filesys_flags |= MS_RDONLY;
483 } else if (strncmp(data, "rw", 2) == 0) {
484 *filesys_flags &= ~MS_RDONLY;
485 } /* else if (strnicmp(data, "port", 4) == 0) {
486 if (value && *value) {
488 simple_strtoul(value, &value, 0);
490 } else if (strnicmp(data, "rsize", 5) == 0) {
491 if (value && *value) {
493 simple_strtoul(value, &value, 0);
495 } else if (strnicmp(data, "wsize", 5) == 0) {
496 if (value && *value) {
498 simple_strtoul(value, &value, 0);
500 } else if (strnicmp(data, "version", 3) == 0) {
502 printf("CIFS: Unknown mount option %s\n",data);
503 } */ /* nothing to do on those four mount options above.
504 Just pass to kernel and ignore them here */
506 /* move to next option */
507 data = next_keyword+1;
509 /* put overwritten equals sign back */
515 /* put previous overwritten comma back */
524 /* Note that caller frees the returned buffer if necessary */
525 char * parse_server(char ** punc_name)
527 char * unc_name = *punc_name;
528 int length = strnlen(unc_name,1024);
530 char * ipaddress_string = NULL;
531 struct hostent * host_entry;
532 struct in_addr server_ipaddr;
536 printf("mount error: UNC name too long");
539 if (strncasecmp("cifs://",unc_name,7) == 0)
540 return parse_cifs_url(unc_name+7);
541 if (strncasecmp("smb://",unc_name,6) == 0) {
542 return parse_cifs_url(unc_name+6);
546 /* BB add code to find DFS root here */
547 printf("\nMounting the DFS root for domain not implemented yet");
550 if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
551 /* check for nfs syntax ie server:share */
552 share = strchr(unc_name,':');
555 *punc_name = malloc(length+3);
557 strncpy((*punc_name)+2,unc_name,length);
558 unc_name = *punc_name;
559 unc_name[length+2] = 0;
560 goto continue_unc_parsing;
562 printf("mount error: improperly formatted UNC name.");
563 printf(" %s does not begin with \\\\ or //\n",unc_name);
567 continue_unc_parsing:
571 if ((share = strchr(unc_name, '/')) ||
572 (share = strchr(unc_name,'\\'))) {
573 *share = 0; /* temporarily terminate the string */
576 host_entry = gethostbyname(unc_name);
578 *(share - 1) = '/'; /* put the slash back */
581 printf("ip address specified explicitly\n");
584 if(host_entry == NULL) {
585 printf("mount error: could not find target server. TCP name %s not found ", unc_name);
586 printf(" rc = %d\n",rc);
589 /* BB should we pass an alternate version of the share name as Unicode */
590 /* BB what about ipv6? BB */
591 /* BB add retries with alternate servers in list */
593 memcpy(&server_ipaddr.s_addr, host_entry->h_addr, 4);
595 ipaddress_string = inet_ntoa(server_ipaddr);
596 if(ipaddress_string == NULL) {
597 printf("mount error: could not get valid ip address for target server\n");
600 return ipaddress_string;
603 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
604 printf("Mounting the DFS root for a particular server not implemented yet\n");
611 static struct option longopts[] = {
612 { "all", 0, NULL, 'a' },
613 { "help",0, NULL, 'h' },
614 { "move",0, NULL, 'm' },
615 { "bind",0, NULL, 'b' },
616 { "read-only", 0, NULL, 'r' },
617 { "ro", 0, NULL, 'r' },
618 { "verbose", 0, NULL, 'v' },
619 { "version", 0, NULL, 'V' },
620 { "read-write", 0, NULL, 'w' },
621 { "rw", 0, NULL, 'w' },
622 { "options", 1, NULL, 'o' },
623 { "type", 1, NULL, 't' },
624 { "rsize",1, NULL, 'R' },
625 { "wsize",1, NULL, 'W' },
626 { "uid", 1, NULL, '1'},
627 { "gid", 1, NULL, '2'},
628 { "user",1,NULL,'u'},
629 { "username",1,NULL,'u'},
631 { "domain",1,NULL,'d'},
632 { "password",1,NULL,'p'},
633 { "pass",1,NULL,'p'},
634 { "credentials",1,NULL,'c'},
635 { "port",1,NULL,'P'},
636 /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
640 int main(int argc, char ** argv)
643 int flags = MS_MANDLOCK; /* no need to set legacy MS_MGC_VAL */
644 char * orgoptions = NULL;
645 char * share_name = NULL;
646 char * domain_name = NULL;
647 char * ipaddr = NULL;
651 char * resolved_path;
661 int retry = 0; /* set when we have to retry mount with uppercase */
663 struct utsname sysinfo;
664 struct mntent mountent;
667 /* setlocale(LC_ALL, "");
668 bindtextdomain(PACKAGE, LOCALEDIR);
669 textdomain(PACKAGE); */
672 thisprogram = argv[0];
674 if(thisprogram == NULL)
675 thisprogram = "mount.cifs";
678 /* BB add workstation name and domain and pass down */
680 /* #ifdef _GNU_SOURCE
681 printf(" node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
684 share_name = argv[1];
685 mountpoint = argv[2];
687 /* add sharename in opts string as unc= parm */
689 while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsSU:vVwt:",
690 longopts, NULL)) != -1) {
692 /* No code to do the following options yet */
694 list_with_volumelabel = 1;
697 volumelabel = optarg;
717 orgoptions = strdup(optarg);
719 case 'r': /* mount readonly */
729 printf ("mount.cifs version: %s.%s%s\n",
730 MOUNT_CIFS_VERSION_MAJOR,
731 MOUNT_CIFS_VERSION_MINOR,
732 MOUNT_CIFS_VENDOR_SUFFIX);
734 memset(mountpassword,0,64);
741 rsize = atoi(optarg) ;
744 wsize = atoi(optarg);
757 domain_name = optarg;
760 if(mountpassword == NULL)
761 mountpassword = calloc(65,1);
764 strncpy(mountpassword,optarg,64);
768 get_password_from_file(0 /* stdin */,NULL);
773 printf("unknown mount option %c\n",c);
782 if (getenv("PASSWD")) {
783 if(mountpassword == NULL)
784 mountpassword = calloc(65,1);
786 strncpy(mountpassword,getenv("PASSWD"),64);
789 } else if (getenv("PASSWD_FD")) {
790 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
791 } else if (getenv("PASSWD_FILE")) {
792 get_password_from_file(0, getenv("PASSWD_FILE"));
795 if (orgoptions && parse_options(orgoptions, &flags))
798 ipaddr = parse_server(&share_name);
799 if((ipaddr == NULL) && (got_ip == 0)) {
800 printf("No ip address specified and hostname not found\n");
805 /* BB save off path and pop after mount returns? */
806 resolved_path = malloc(PATH_MAX+1);
808 /* Note that if we can not canonicalize the name, we get
809 another chance to see if it is valid when we chdir to it */
810 if (realpath(mountpoint, resolved_path)) {
811 mountpoint = resolved_path;
814 if(chdir(mountpoint)) {
815 printf("mount error: can not change directory into mount target %s\n",mountpoint);
819 if(stat (".", &statbuf)) {
820 printf("mount error: mount point %s does not exist\n",mountpoint);
824 if (S_ISDIR(statbuf.st_mode) == 0) {
825 printf("mount error: mount point %s is not a directory\n",mountpoint);
829 if((getuid() != 0) && (geteuid() == 0)) {
830 if((statbuf.st_uid == getuid()) && (S_IRWXU == (statbuf.st_mode & S_IRWXU))) {
831 #ifndef CIFS_ALLOW_USR_SUID
832 /* Do not allow user mounts to control suid flag
833 for mount unless explicitly built that way */
834 flags |= MS_NOSUID | MS_NODEV;
837 printf("mount error: permission denied or not superuser and mount.cifs not installed SUID\n");
843 user_name = getusername();
845 if(got_password == 0) {
846 mountpassword = getpass("Password: "); /* BB obsolete */
849 /* FIXME launch daemon (handles dfs name resolution and credential change)
850 remember to clear parms and overwrite password field before launching */
853 optlen = strlen(orgoptions);
858 optlen += strlen(share_name) + 4;
860 optlen += strlen(user_name) + 6;
862 optlen += strlen(ipaddr) + 4;
864 optlen += strlen(mountpassword) + 6;
865 options = malloc(optlen + 10);
867 if(options == NULL) {
868 printf("Could not allocate memory for mount options\n");
874 strncat(options,"unc=",4);
875 strcat(options,share_name);
876 /* scan backwards and reverse direction of slash */
877 temp = strrchr(options, '/');
878 if(temp > options + 6)
881 strncat(options,",ip=",4);
882 strcat(options,ipaddr);
885 strncat(options,",user=",6);
886 strcat(options,user_name);
889 strncat(options,",pass=",6);
890 strcat(options,mountpassword);
892 strncat(options,",ver=",5);
893 strcat(options,MOUNT_CIFS_VERSION_MAJOR);
897 strcat(options,orgoptions);
900 printf("\nmount.cifs kernel mount options %s \n",options);
901 if(mount(share_name, mountpoint, "cifs", flags, options)) {
902 /* remember to kill daemon on error */
907 printf("mount failed but no error number set\n");
910 printf("mount error: cifs filesystem not supported by the system\n");
916 while (*tmp && !(((unsigned char)tmp[0]) & 0x80)) {
917 *tmp = toupper((unsigned char)*tmp);
921 printf("retrying with upper case share name\n");
927 printf("mount error %d = %s\n",errno,strerror(errno));
929 printf("Refer to the mount.cifs(8) manual page (e.g.man mount.cifs)\n");
931 memset(mountpassword,0,64);
935 pmntfile = setmntent(MOUNTED, "a+");
937 mountent.mnt_fsname = share_name;
938 mountent.mnt_dir = mountpoint;
939 mountent.mnt_type = "cifs";
940 mountent.mnt_opts = malloc(220);
941 if(mountent.mnt_opts) {
942 char * mount_user = getusername();
943 memset(mountent.mnt_opts,0,200);
944 if(flags & MS_RDONLY)
945 strcat(mountent.mnt_opts,"ro");
947 strcat(mountent.mnt_opts,"rw");
948 if(flags & MS_MANDLOCK)
949 strcat(mountent.mnt_opts,",mand");
951 strcat(mountent.mnt_opts,",nomand");
952 if(flags & MS_NOEXEC)
953 strcat(mountent.mnt_opts,",noexec");
954 if(flags & MS_NOSUID)
955 strcat(mountent.mnt_opts,",nosuid");
957 strcat(mountent.mnt_opts,",nodev");
958 if(flags & MS_SYNCHRONOUS)
959 strcat(mountent.mnt_opts,",synch");
962 strcat(mountent.mnt_opts,",user=");
963 strcat(mountent.mnt_opts,mount_user);
968 mountent.mnt_freq = 0;
969 mountent.mnt_passno = 0;
970 rc = addmntent(pmntfile,&mountent);
972 if(mountent.mnt_opts)
973 free(mountent.mnt_opts);
975 printf("could not update mount table\n");
979 memset(mountpassword,0,64);
984 memset(options,0,optlen);
989 memset(orgoptions,0,orgoptlen);
996 if(free_share_name) {