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 "5"
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("\nLess commonly used options:");
81 printf("\n\tcredentials=<filename>,guest,perm,noperm,setuids,nosetuids,\n\trw,ro,sep=<char>,iocharset=<codepage>,suid,nosuid,exec,noexec");
82 printf("\n\nOptions not needed for servers supporting CIFS Unix extensions (e.g. most Samba versions):");
83 printf("\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>");
84 printf("\n\nRarely used options:");
85 printf("\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,dev,nodev");
86 printf("\n\nOptions are described in more detail in the manual page");
87 printf("\n\tman 8 mount.cifs\n");
88 printf("\nTo display the version number of the mount helper:");
89 printf("\n\t%s -V\n",thisprogram);
92 memset(mountpassword,0,64);
98 /* caller frees username if necessary */
99 static char * getusername(void) {
100 char *username = NULL;
101 struct passwd *password = getpwuid(getuid());
104 username = password->pw_name;
109 char * parse_cifs_url(char * unc_name)
111 printf("\nMounting cifs URL not implemented yet. Attempt to mount %s\n",unc_name);
115 static int open_cred_file(char * file_name)
121 fs = fopen(file_name,"r");
124 line_buf = malloc(4096);
128 while(fgets(line_buf,4096,fs)) {
129 /* parse line from credential file */
131 /* eat leading white space */
132 for(i=0;i<4086;i++) {
133 if((line_buf[i] != ' ') && (line_buf[i] != '\t'))
135 /* if whitespace - skip past it */
137 if (strncasecmp("username",line_buf+i,8) == 0) {
138 temp_val = strchr(line_buf + i,'=');
140 /* go past equals sign */
142 for(length = 0;length<4087;length++) {
143 if(temp_val[length] == '\n')
147 printf("mount.cifs failed due to malformed username in credentials file");
148 memset(line_buf,0,4096);
150 memset(mountpassword,0,64);
155 user_name = calloc(1 + length,1);
156 /* BB adding free of user_name string before exit,
157 not really necessary but would be cleaner */
158 strncpy(user_name,temp_val, length);
161 } else if (strncasecmp("password",line_buf+i,8) == 0) {
162 temp_val = strchr(line_buf+i,'=');
164 /* go past equals sign */
166 for(length = 0;length<65;length++) {
167 if(temp_val[length] == '\n')
171 printf("mount.cifs failed: password in credentials file too long\n");
172 memset(line_buf,0, 4096);
174 memset(mountpassword,0,64);
178 if(mountpassword == NULL) {
179 mountpassword = calloc(65,1);
181 memset(mountpassword,0,64);
183 /* BB add handling for commas in password here */
184 strncpy(mountpassword,temp_val,length);
193 memset(line_buf,0,4096);
199 static int get_password_from_file(int file_descript, char * filename)
205 if(mountpassword == NULL)
206 mountpassword = calloc(65,1);
208 memset(mountpassword, 0, 64);
210 if(filename != NULL) {
211 file_descript = open(filename, O_RDONLY);
212 if(file_descript < 0) {
213 printf("mount.cifs failed. %s attempting to open password file %s\n",
214 strerror(errno),filename);
218 /* else file already open and fd provided */
221 rc = read(file_descript,&c,1);
223 printf("mount.cifs failed. Error %s reading password file\n",strerror(errno));
224 memset(mountpassword,0,64);
226 close(file_descript);
229 if(mountpassword[0] == 0) {
231 printf("\nWarning: null password used since cifs password file empty");
234 } else /* read valid character */ {
235 if((c == 0) || (c == '\n')) {
238 mountpassword[i] = c;
241 if((i == 64) && (verboseflag)) {
242 printf("\nWarning: password longer than 64 characters specified in cifs password file");
245 if(filename != NULL) {
246 close(file_descript);
252 static int parse_options(char * options, int * filesys_flags)
255 char * percent_char = NULL;
257 char * next_keyword = NULL;
266 printf("\n parsing options: %s", options);
268 /* while ((data = strsep(&options, ",")) != NULL) { */
269 while(data != NULL) {
270 /* check if ends with trailing comma */
274 /* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
275 /* data = next keyword */
276 /* value = next value ie stuff after equal sign */
278 next_keyword = strchr(data,',');
280 /* temporarily null terminate end of keyword=value pair */
287 /* temporarily null terminate keyword to make keyword and value distinct */
288 if ((value = strchr(data, '=')) != NULL) {
293 if (strncmp(data, "user", 4) == 0) {
294 if (!value || !*value) {
295 if(data[4] == '\0') {
297 printf("\nskipping empty user mount parameter\n");
298 /* remove the parm since it would otherwise be confusing
299 to the kernel code which would think it was a real username */
304 /* BB remove it from mount line so as not to confuse kernel code */
306 printf("username specified with no parameter\n");
307 return 1; /* needs_arg; */
310 if (strnlen(value, 260) < 260) {
312 percent_char = strchr(value,'%');
315 if(mountpassword == NULL)
316 mountpassword = calloc(65,1);
319 printf("\nmount.cifs warning - password specified twice\n");
322 strncpy(mountpassword, percent_char,64);
323 /* remove password from username */
324 while(*percent_char != 0) {
331 printf("username too long\n");
335 } else if (strncmp(data, "pass", 4) == 0) {
336 if (!value || !*value) {
338 printf("\npassword specified twice, ignoring second\n");
341 } else if (strnlen(value, 17) < 17) {
343 printf("\nmount.cifs warning - password specified twice\n");
346 printf("password too long\n");
349 } else if (strncmp(data, "ip", 2) == 0) {
350 if (!value || !*value) {
351 printf("target ip address argument missing");
352 } else if (strnlen(value, 35) < 35) {
354 printf("ip address %s override specified\n",value);
357 printf("ip address too long\n");
360 } else if ((strncmp(data, "unc", 3) == 0)
361 || (strncmp(data, "target", 6) == 0)
362 || (strncmp(data, "path", 4) == 0)) {
363 if (!value || !*value) {
364 printf("invalid path to network resource\n");
365 return 1; /* needs_arg; */
366 } else if(strnlen(value,5) < 5) {
367 printf("UNC name too short");
370 if (strnlen(value, 300) < 300) {
372 if (strncmp(value, "//", 2) == 0) {
374 printf("unc name specified twice, ignoring second\n");
377 } else if (strncmp(value, "\\\\", 2) != 0) {
378 printf("UNC Path does not begin with // or \\\\ \n");
382 printf("unc name specified twice, ignoring second\n");
387 printf("CIFS: UNC name too long\n");
390 } else if ((strncmp(data, "domain", 3) == 0)
391 || (strncmp(data, "workgroup", 5) == 0)) {
392 if (!value || !*value) {
393 printf("CIFS: invalid domain name\n");
394 return 1; /* needs_arg; */
396 if (strnlen(value, 65) < 65) {
399 printf("domain name too long\n");
402 } else if (strncmp(data, "cred", 4) == 0) {
403 if (value && *value) {
404 rc = open_cred_file(value);
406 printf("error %d opening credential file %s\n",rc, value);
410 printf("invalid credential file name specified\n");
413 } else if (strncmp(data, "uid", 3) == 0) {
414 if (value && *value) {
417 } else if (strncmp(data, "gid", 3) == 0) {
418 if (value && *value) {
421 /* fmask and dmask synonyms for people used to smbfs syntax */
422 } else if (strcmp(data, "file_mode") == 0 || strcmp(data, "fmask")==0) {
423 if (!value || !*value) {
424 printf ("Option '%s' requires a numerical argument\n", data);
428 if (value[0] != '0') {
429 printf ("WARNING: '%s' not expressed in octal.\n", data);
432 if (strcmp (data, "fmask") == 0) {
433 printf ("WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
434 data = "file_mode"; /* BB fix this */
436 } else if (strcmp(data, "dir_mode") == 0 || strcmp(data, "dmask")==0) {
437 if (!value || !*value) {
438 printf ("Option '%s' requires a numerical argument\n", data);
442 if (value[0] != '0') {
443 printf ("WARNING: '%s' not expressed in octal.\n", data);
446 if (strcmp (data, "dmask") == 0) {
447 printf ("WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
450 /* the following eight mount options should be
451 stripped out from what is passed into the kernel
452 since these eight options are best passed as the
453 mount flags rather than redundantly to the kernel
454 and could generate spurious warnings depending on the
455 level of the corresponding cifs vfs kernel code */
456 } else if (strncmp(data, "nosuid", 6) == 0) {
457 *filesys_flags |= MS_NOSUID;
458 } else if (strncmp(data, "suid", 4) == 0) {
459 *filesys_flags &= ~MS_NOSUID;
460 } else if (strncmp(data, "nodev", 5) == 0) {
461 *filesys_flags |= MS_NODEV;
462 } else if (strncmp(data, "dev", 3) == 0) {
463 *filesys_flags &= ~MS_NODEV;
464 } else if (strncmp(data, "noexec", 6) == 0) {
465 *filesys_flags |= MS_NOEXEC;
466 } else if (strncmp(data, "exec", 4) == 0) {
467 *filesys_flags &= ~MS_NOEXEC;
468 } else if (strncmp(data, "guest", 5) == 0) {
470 /* remove the parm since it would otherwise be logged by kern */
476 } else if (strncmp(data, "ro", 2) == 0) {
477 *filesys_flags |= MS_RDONLY;
478 } else if (strncmp(data, "rw", 2) == 0) {
479 *filesys_flags &= ~MS_RDONLY;
480 } /* else if (strnicmp(data, "port", 4) == 0) {
481 if (value && *value) {
483 simple_strtoul(value, &value, 0);
485 } else if (strnicmp(data, "rsize", 5) == 0) {
486 if (value && *value) {
488 simple_strtoul(value, &value, 0);
490 } else if (strnicmp(data, "wsize", 5) == 0) {
491 if (value && *value) {
493 simple_strtoul(value, &value, 0);
495 } else if (strnicmp(data, "version", 3) == 0) {
497 printf("CIFS: Unknown mount option %s\n",data);
498 } */ /* nothing to do on those four mount options above.
499 Just pass to kernel and ignore them here */
501 /* move to next option */
502 data = next_keyword+1;
504 /* put overwritten equals sign back */
510 /* put previous overwritten comma back */
519 /* Note that caller frees the returned buffer if necessary */
520 char * parse_server(char ** punc_name)
522 char * unc_name = *punc_name;
523 int length = strnlen(unc_name,1024);
525 char * ipaddress_string = NULL;
526 struct hostent * host_entry;
527 struct in_addr server_ipaddr;
531 printf("mount error: UNC name too long");
534 if (strncasecmp("cifs://",unc_name,7) == 0)
535 return parse_cifs_url(unc_name+7);
536 if (strncasecmp("smb://",unc_name,6) == 0) {
537 return parse_cifs_url(unc_name+6);
541 /* BB add code to find DFS root here */
542 printf("\nMounting the DFS root for domain not implemented yet");
545 if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
546 /* check for nfs syntax ie server:share */
547 share = strchr(unc_name,':');
550 *punc_name = malloc(length+3);
552 strncpy((*punc_name)+2,unc_name,length);
553 unc_name = *punc_name;
554 unc_name[length+2] = 0;
555 goto continue_unc_parsing;
557 printf("mount error: improperly formatted UNC name.");
558 printf(" %s does not begin with \\\\ or //\n",unc_name);
562 continue_unc_parsing:
566 if ((share = strchr(unc_name, '/')) ||
567 (share = strchr(unc_name,'\\'))) {
568 *share = 0; /* temporarily terminate the string */
571 host_entry = gethostbyname(unc_name);
573 *(share - 1) = '/'; /* put the slash back */
576 printf("ip address specified explicitly\n");
579 if(host_entry == NULL) {
580 printf("mount error: could not find target server. TCP name %s not found ", unc_name);
581 printf(" rc = %d\n",rc);
584 /* BB should we pass an alternate version of the share name as Unicode */
585 /* BB what about ipv6? BB */
586 /* BB add retries with alternate servers in list */
588 memcpy(&server_ipaddr.s_addr, host_entry->h_addr, 4);
590 ipaddress_string = inet_ntoa(server_ipaddr);
591 if(ipaddress_string == NULL) {
592 printf("mount error: could not get valid ip address for target server\n");
595 return ipaddress_string;
598 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
599 printf("Mounting the DFS root for a particular server not implemented yet\n");
606 static struct option longopts[] = {
607 { "all", 0, NULL, 'a' },
608 { "help",0, NULL, 'h' },
609 { "move",0, NULL, 'm' },
610 { "bind",0, NULL, 'b' },
611 { "read-only", 0, NULL, 'r' },
612 { "ro", 0, NULL, 'r' },
613 { "verbose", 0, NULL, 'v' },
614 { "version", 0, NULL, 'V' },
615 { "read-write", 0, NULL, 'w' },
616 { "rw", 0, NULL, 'w' },
617 { "options", 1, NULL, 'o' },
618 { "type", 1, NULL, 't' },
619 { "rsize",1, NULL, 'R' },
620 { "wsize",1, NULL, 'W' },
621 { "uid", 1, NULL, '1'},
622 { "gid", 1, NULL, '2'},
623 { "user",1,NULL,'u'},
624 { "username",1,NULL,'u'},
626 { "domain",1,NULL,'d'},
627 { "password",1,NULL,'p'},
628 { "pass",1,NULL,'p'},
629 { "credentials",1,NULL,'c'},
630 { "port",1,NULL,'P'},
631 /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
635 int main(int argc, char ** argv)
638 int flags = MS_MANDLOCK; /* no need to set legacy MS_MGC_VAL */
639 char * orgoptions = NULL;
640 char * share_name = NULL;
641 char * domain_name = NULL;
642 char * ipaddr = NULL;
646 char * resolved_path;
657 struct utsname sysinfo;
658 struct mntent mountent;
661 /* setlocale(LC_ALL, "");
662 bindtextdomain(PACKAGE, LOCALEDIR);
663 textdomain(PACKAGE); */
666 thisprogram = argv[0];
668 if(thisprogram == NULL)
669 thisprogram = "mount.cifs";
672 /* BB add workstation name and domain and pass down */
674 /* #ifdef _GNU_SOURCE
675 printf(" node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
678 share_name = argv[1];
679 mountpoint = argv[2];
681 /* add sharename in opts string as unc= parm */
683 while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsSU:vVwt:",
684 longopts, NULL)) != -1) {
686 /* No code to do the following options yet */
688 list_with_volumelabel = 1;
691 volumelabel = optarg;
711 orgoptions = strdup(optarg);
713 case 'r': /* mount readonly */
723 printf ("mount.cifs version: %s.%s%s\n",
724 MOUNT_CIFS_VERSION_MAJOR,
725 MOUNT_CIFS_VERSION_MINOR,
726 MOUNT_CIFS_VENDOR_SUFFIX);
728 memset(mountpassword,0,64);
735 rsize = atoi(optarg) ;
738 wsize = atoi(optarg);
751 domain_name = optarg;
754 if(mountpassword == NULL)
755 mountpassword = calloc(65,1);
758 strncpy(mountpassword,optarg,64);
762 get_password_from_file(0 /* stdin */,NULL);
767 printf("unknown mount option %c\n",c);
776 if (getenv("PASSWD")) {
777 if(mountpassword == NULL)
778 mountpassword = calloc(65,1);
780 strncpy(mountpassword,getenv("PASSWD"),64);
783 } else if (getenv("PASSWD_FD")) {
784 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
785 } else if (getenv("PASSWD_FILE")) {
786 get_password_from_file(0, getenv("PASSWD_FILE"));
789 if (orgoptions && parse_options(orgoptions, &flags))
792 ipaddr = parse_server(&share_name);
793 if((ipaddr == NULL) && (got_ip == 0)) {
794 printf("No ip address specified and hostname not found\n");
799 /* BB save off path and pop after mount returns? */
800 resolved_path = malloc(PATH_MAX+1);
802 /* Note that if we can not canonicalize the name, we get
803 another chance to see if it is valid when we chdir to it */
804 if (realpath(mountpoint, resolved_path)) {
805 mountpoint = resolved_path;
808 if(chdir(mountpoint)) {
809 printf("mount error: can not change directory into mount target %s\n",mountpoint);
813 if(stat (".", &statbuf)) {
814 printf("mount error: mount point %s does not exist\n",mountpoint);
818 if (S_ISDIR(statbuf.st_mode) == 0) {
819 printf("mount error: mount point %s is not a directory\n",mountpoint);
823 if((getuid() != 0) && (geteuid() == 0)) {
824 if((statbuf.st_uid == getuid()) && (S_IRWXU == (statbuf.st_mode & S_IRWXU))) {
825 #ifndef CIFS_ALLOW_USR_SUID
826 /* Do not allow user mounts to control suid flag
827 for mount unless explicitly built that way */
828 flags |= MS_NOSUID | MS_NODEV;
831 printf("mount error: permission denied or not superuser and mount.cifs not installed SUID\n");
837 user_name = getusername();
839 if(got_password == 0) {
840 mountpassword = getpass("Password: "); /* BB obsolete */
843 /* FIXME launch daemon (handles dfs name resolution and credential change)
844 remember to clear parms and overwrite password field before launching */
846 optlen = strlen(orgoptions);
851 optlen += strlen(share_name) + 4;
853 optlen += strlen(user_name) + 6;
855 optlen += strlen(ipaddr) + 4;
857 optlen += strlen(mountpassword) + 6;
858 options = malloc(optlen + 10);
860 if(options == NULL) {
861 printf("Could not allocate memory for mount options\n");
867 strncat(options,"unc=",4);
868 strcat(options,share_name);
869 /* scan backwards and reverse direction of slash */
870 temp = strrchr(options, '/');
871 if(temp > options + 6)
874 strncat(options,",ip=",4);
875 strcat(options,ipaddr);
878 strncat(options,",user=",6);
879 strcat(options,user_name);
882 strncat(options,",pass=",6);
883 strcat(options,mountpassword);
885 strncat(options,",ver=",5);
886 strcat(options,MOUNT_CIFS_VERSION_MAJOR);
890 strcat(options,orgoptions);
893 printf("\nmount.cifs kernel mount options %s \n",options);
894 if(mount(share_name, mountpoint, "cifs", flags, options)) {
895 /* remember to kill daemon on error */
898 printf("mount failed but no error number set\n");
901 printf("mount error: cifs filesystem not supported by the system\n");
904 printf("mount error %d = %s\n",errno,strerror(errno));
906 printf("Refer to the mount.cifs(8) manual page (e.g.man mount.cifs)\n");
908 memset(mountpassword,0,64);
912 pmntfile = setmntent(MOUNTED, "a+");
914 mountent.mnt_fsname = share_name;
915 mountent.mnt_dir = mountpoint;
916 mountent.mnt_type = "cifs";
917 mountent.mnt_opts = malloc(220);
918 if(mountent.mnt_opts) {
919 char * mount_user = getusername();
920 memset(mountent.mnt_opts,0,200);
921 if(flags & MS_RDONLY)
922 strcat(mountent.mnt_opts,"ro");
924 strcat(mountent.mnt_opts,"rw");
925 if(flags & MS_MANDLOCK)
926 strcat(mountent.mnt_opts,",mand");
928 strcat(mountent.mnt_opts,",nomand");
929 if(flags & MS_NOEXEC)
930 strcat(mountent.mnt_opts,",noexec");
931 if(flags & MS_NOSUID)
932 strcat(mountent.mnt_opts,",nosuid");
934 strcat(mountent.mnt_opts,",nodev");
935 if(flags & MS_SYNCHRONOUS)
936 strcat(mountent.mnt_opts,",synch");
939 strcat(mountent.mnt_opts,",user=");
940 strcat(mountent.mnt_opts,mount_user);
945 mountent.mnt_freq = 0;
946 mountent.mnt_passno = 0;
947 rc = addmntent(pmntfile,&mountent);
949 if(mountent.mnt_opts)
950 free(mountent.mnt_opts);
952 printf("could not update mount table\n");
956 memset(mountpassword,0,64);
961 memset(options,0,optlen);
966 memset(orgoptions,0,orgoptlen);
973 if(free_share_name) {