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 "2"
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\nOptions:\n");
78 printf("\tuser=<arg>\n\tpass=<arg>\n\tdom=<arg>\n");
79 printf("\nOther less commonly used options are described in the manual page");
80 printf("\n\tman 8 mount.cifs\n");
81 printf("\nTo display the version number of the mount helper:");
82 printf("\n\t%s -V\n",thisprogram);
85 memset(mountpassword,0,64);
91 /* caller frees username if necessary */
92 static char * getusername(void) {
93 char *username = NULL;
94 struct passwd *password = getpwuid(getuid());
97 username = password->pw_name;
102 char * parse_cifs_url(char * unc_name)
104 printf("\nMounting cifs URL not implemented yet. Attempt to mount %s\n",unc_name);
108 static int open_cred_file(char * file_name)
114 fs = fopen(file_name,"r");
117 line_buf = malloc(4096);
121 while(fgets(line_buf,4096,fs)) {
122 /* parse line from credential file */
124 /* eat leading white space */
125 for(i=0;i<4086;i++) {
126 if((line_buf[i] != ' ') && (line_buf[i] != '\t'))
128 /* if whitespace - skip past it */
130 if (strncasecmp("username",line_buf+i,8) == 0) {
131 temp_val = strchr(line_buf + i,'=');
133 /* go past equals sign */
135 for(length = 0;length<4087;length++) {
136 if(temp_val[length] == '\n')
140 printf("mount.cifs failed due to malformed username in credentials file");
141 memset(line_buf,0,4096);
143 memset(mountpassword,0,64);
148 user_name = calloc(1 + length,1);
149 /* BB adding free of user_name string before exit,
150 not really necessary but would be cleaner */
151 strncpy(user_name,temp_val, length);
154 } else if (strncasecmp("password",line_buf+i,8) == 0) {
155 temp_val = strchr(line_buf+i,'=');
157 /* go past equals sign */
159 for(length = 0;length<65;length++) {
160 if(temp_val[length] == '\n')
164 printf("mount.cifs failed: password in credentials file too long\n");
165 memset(line_buf,0, 4096);
167 memset(mountpassword,0,64);
171 if(mountpassword == NULL) {
172 mountpassword = calloc(65,1);
174 memset(mountpassword,0,64);
176 /* BB add handling for commas in password here */
177 strncpy(mountpassword,temp_val,length);
186 memset(line_buf,0,4096);
192 static int get_password_from_file(int file_descript, char * filename)
198 if(mountpassword == NULL)
199 mountpassword = calloc(65,1);
201 memset(mountpassword, 0, 64);
203 if(filename != NULL) {
204 file_descript = open(filename, O_RDONLY);
205 if(file_descript < 0) {
206 printf("mount.cifs failed. %s attempting to open password file %s\n",
207 strerror(errno),filename);
211 /* else file already open and fd provided */
214 rc = read(file_descript,&c,1);
216 printf("mount.cifs failed. Error %s reading password file\n",strerror(errno));
217 memset(mountpassword,0,64);
219 close(file_descript);
222 if(mountpassword[0] == 0) {
224 printf("\nWarning: null password used since cifs password file empty");
227 } else /* read valid character */ {
228 if((c == 0) || (c == '\n')) {
231 mountpassword[i] = c;
234 if((i == 64) && (verboseflag)) {
235 printf("\nWarning: password longer than 64 characters specified in cifs password file");
238 if(filename != NULL) {
239 close(file_descript);
245 static int parse_options(char * options, int * filesys_flags)
248 char * percent_char = 0;
250 char * next_keyword = 0;
259 printf("\n parsing options: %s", options);
261 /* while ((data = strsep(&options, ",")) != NULL) { */
262 while(data != NULL) {
263 /* check if ends with trailing comma */
267 /* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
268 /* data = next keyword */
269 /* value = next value ie stuff after equal sign */
271 next_keyword = strchr(data,',');
273 /* temporarily null terminate end of keyword=value pair */
280 /* temporarily null terminate keyword to make keyword and value distinct */
281 if ((value = strchr(data, '=')) != NULL) {
286 if (strncmp(data, "user", 4) == 0) {
287 if (!value || !*value) {
288 printf("invalid or missing username\n");
289 return 1; /* needs_arg; */
291 if (strnlen(value, 260) < 260) {
293 percent_char = strchr(value,'%');
296 if(mountpassword == NULL)
297 mountpassword = calloc(65,1);
300 printf("\nmount.cifs warning - password specified twice\n");
303 strncpy(mountpassword, percent_char,64);
304 /* remove password from username */
305 while(*percent_char != 0) {
312 printf("username too long\n");
315 } else if (strncmp(data, "pass", 4) == 0) {
316 if (!value || !*value) {
318 printf("\npassword specified twice, ignoring second\n");
321 } else if (strnlen(value, 17) < 17) {
323 printf("\nmount.cifs warning - password specified twice\n");
326 printf("password too long\n");
329 } else if (strncmp(data, "ip", 2) == 0) {
330 if (!value || !*value) {
331 printf("target ip address argument missing");
332 } else if (strnlen(value, 35) < 35) {
335 printf("ip address too long\n");
338 } else if ((strncmp(data, "unc", 3) == 0)
339 || (strncmp(data, "target", 6) == 0)
340 || (strncmp(data, "path", 4) == 0)) {
341 if (!value || !*value) {
342 printf("invalid path to network resource\n");
343 return 1; /* needs_arg; */
344 } else if(strnlen(value,5) < 5) {
345 printf("UNC name too short");
348 if (strnlen(value, 300) < 300) {
350 if (strncmp(value, "//", 2) == 0) {
352 printf("unc name specified twice, ignoring second\n");
355 } else if (strncmp(value, "\\\\", 2) != 0) {
356 printf("UNC Path does not begin with // or \\\\ \n");
360 printf("unc name specified twice, ignoring second\n");
365 printf("CIFS: UNC name too long\n");
368 } else if ((strncmp(data, "domain", 3) == 0)
369 || (strncmp(data, "workgroup", 5) == 0)) {
370 if (!value || !*value) {
371 printf("CIFS: invalid domain name\n");
372 return 1; /* needs_arg; */
374 if (strnlen(value, 65) < 65) {
377 printf("domain name too long\n");
380 } else if (strncmp(data, "cred", 4) == 0) {
381 if (value && *value) {
382 rc = open_cred_file(value);
384 printf("error %d opening credential file %s\n",rc, value);
388 printf("invalid credential file name specified\n");
391 } else if (strncmp(data, "uid", 3) == 0) {
392 if (value && *value) {
395 } else if (strncmp(data, "gid", 3) == 0) {
396 if (value && *value) {
399 /* fmask and dmask synonyms for people used to smbfs syntax */
400 } else if (strcmp(data, "file_mode") == 0 || strcmp(data, "fmask")==0) {
401 if (!value || !*value) {
402 printf ("Option '%s' requires a numerical argument\n", data);
406 if (value[0] != '0') {
407 printf ("WARNING: '%s' not expressed in octal.\n", data);
410 if (strcmp (data, "fmask") == 0) {
411 printf ("WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
412 data = "file_mode"; /* BB fix this */
414 } else if (strcmp(data, "dir_mode") == 0 || strcmp(data, "dmask")==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, "dmask") == 0) {
425 printf ("WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
428 /* the following eight mount options should be
429 stripped out from what is passed into the kernel
430 since these eight options are best passed as the
431 mount flags rather than redundantly to the kernel
432 and could generate spurious warnings depending on the
433 level of the corresponding cifs vfs kernel code */
434 } else if (strncmp(data, "nosuid", 6) == 0) {
435 *filesys_flags |= MS_NOSUID;
436 } else if (strncmp(data, "suid", 4) == 0) {
437 *filesys_flags &= ~MS_NOSUID;
438 } else if (strncmp(data, "nodev", 5) == 0) {
439 *filesys_flags |= MS_NODEV;
440 } else if (strncmp(data, "dev", 3) == 0) {
441 *filesys_flags &= ~MS_NODEV;
442 } else if (strncmp(data, "noexec", 6) == 0) {
443 *filesys_flags |= MS_NOEXEC;
444 } else if (strncmp(data, "exec", 4) == 0) {
445 *filesys_flags &= ~MS_NOEXEC;
446 } else if (strncmp(data, "guest", 5) == 0) {
448 } else if (strncmp(data, "ro", 2) == 0) {
449 *filesys_flags |= MS_RDONLY;
450 } else if (strncmp(data, "rw", 2) == 0) {
451 *filesys_flags &= ~MS_RDONLY;
452 } /* else if (strnicmp(data, "port", 4) == 0) {
453 if (value && *value) {
455 simple_strtoul(value, &value, 0);
457 } else if (strnicmp(data, "rsize", 5) == 0) {
458 if (value && *value) {
460 simple_strtoul(value, &value, 0);
462 } else if (strnicmp(data, "wsize", 5) == 0) {
463 if (value && *value) {
465 simple_strtoul(value, &value, 0);
467 } else if (strnicmp(data, "version", 3) == 0) {
469 printf("CIFS: Unknown mount option %s\n",data);
470 } */ /* nothing to do on those four mount options above.
471 Just pass to kernel and ignore them here */
473 /* move to next option */
474 data = next_keyword+1;
476 /* put overwritten equals sign back */
482 /* put previous overwritten comma back */
491 /* Note that caller frees the returned buffer if necessary */
492 char * parse_server(char * unc_name)
494 int length = strnlen(unc_name,1024);
496 char * ipaddress_string = NULL;
497 struct hostent * host_entry;
498 struct in_addr server_ipaddr;
502 printf("mount error: UNC name too long");
505 if (strncasecmp("cifs://",unc_name,7) == 0)
506 return parse_cifs_url(unc_name+7);
507 if (strncasecmp("smb://",unc_name,6) == 0) {
508 return parse_cifs_url(unc_name+6);
512 /* BB add code to find DFS root here */
513 printf("\nMounting the DFS root for domain not implemented yet");
516 if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
517 printf("mount error: improperly formatted UNC name.");
518 printf(" %s does not begin with \\\\ or //\n",unc_name);
525 if ((share = strchr(unc_name, '/')) ||
526 (share = strchr(unc_name,'\\'))) {
527 *share = 0; /* temporarily terminate the string */
529 host_entry = gethostbyname(unc_name);
530 *(share - 1) = '/'; /* put the slash back */
531 /* rc = getipnodebyname(unc_name, AF_INET, AT_ADDRCONFIG ,&rc);*/
532 if(host_entry == NULL) {
533 printf("mount error: could not find target server. TCP name %s not found ", unc_name);
534 printf(" rc = %d\n",rc);
538 /* BB should we pass an alternate version of the share name as Unicode */
539 /* BB what about ipv6? BB */
540 /* BB add retries with alternate servers in list */
542 memcpy(&server_ipaddr.s_addr, host_entry->h_addr, 4);
544 ipaddress_string = inet_ntoa(server_ipaddr);
545 if(ipaddress_string == NULL) {
546 printf("mount error: could not get valid ip address for target server\n");
549 return ipaddress_string;
552 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
553 printf("Mounting the DFS root for a particular server not implemented yet\n");
560 static struct option longopts[] = {
561 { "all", 0, 0, 'a' },
562 { "help",0, 0, 'h' },
563 { "move",0, 0, 'm' },
564 { "bind",0, 0, 'b' },
565 { "read-only", 0, 0, 'r' },
567 { "verbose", 0, 0, 'v' },
568 { "version", 0, 0, 'V' },
569 { "read-write", 0, 0, 'w' },
571 { "options", 1, 0, 'o' },
572 { "type", 1, 0, 't' },
573 { "rsize",1, 0, 'R' },
574 { "wsize",1, 0, 'W' },
578 { "username",1,0,'u'},
581 { "password",1,0,'p'},
583 { "credentials",1,0,'c'},
585 /* { "uuid",1,0,'U'}, */ /* BB unimplemented */
589 int main(int argc, char ** argv)
592 int flags = MS_MANDLOCK; /* no need to set legacy MS_MGC_VAL */
593 char * orgoptions = NULL;
594 char * share_name = NULL;
595 char * domain_name = NULL;
596 char * ipaddr = NULL;
600 char * resolved_path;
611 struct utsname sysinfo;
612 struct mntent mountent;
615 /* setlocale(LC_ALL, "");
616 bindtextdomain(PACKAGE, LOCALEDIR);
617 textdomain(PACKAGE); */
620 thisprogram = argv[0];
622 if(thisprogram == NULL)
623 thisprogram = "mount.cifs";
626 /* BB add workstation name and domain and pass down */
628 /* #ifdef _GNU_SOURCE
629 printf(" node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
632 share_name = argv[1];
633 mountpoint = argv[2];
635 /* add sharename in opts string as unc= parm */
637 while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsU:vVwt:",
638 longopts, NULL)) != -1) {
640 /* No code to do the following options yet */
642 list_with_volumelabel = 1;
645 volumelabel = optarg;
665 orgoptions = strdup(optarg);
667 case 'r': /* mount readonly */
677 printf ("mount.cifs version: %s.%s%s\n",
678 MOUNT_CIFS_VERSION_MAJOR,
679 MOUNT_CIFS_VERSION_MINOR,
680 MOUNT_CIFS_VENDOR_SUFFIX);
682 memset(mountpassword,0,64);
689 rsize = atoi(optarg) ;
692 wsize = atoi(optarg);
705 domain_name = optarg;
708 if(mountpassword == NULL)
709 mountpassword = calloc(65,1);
712 strncpy(mountpassword,optarg,64);
718 printf("unknown mount option %c\n",c);
727 if (getenv("PASSWD")) {
728 if(mountpassword == NULL)
729 mountpassword = calloc(65,1);
731 strncpy(mountpassword,getenv("PASSWD"),64);
734 } else if (getenv("PASSWD_FD")) {
735 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
736 } else if (getenv("PASSWD_FILE")) {
737 get_password_from_file(0, getenv("PASSWD_FILE"));
740 ipaddr = parse_server(share_name);
745 if (orgoptions && parse_options(orgoptions, &flags))
748 /* BB save off path and pop after mount returns? */
749 resolved_path = malloc(PATH_MAX+1);
751 /* Note that if we can not canonicalize the name, we get
752 another chance to see if it is valid when we chdir to it */
753 if (realpath(mountpoint, resolved_path)) {
754 mountpoint = resolved_path;
757 if(chdir(mountpoint)) {
758 printf("mount error: can not change directory into mount target %s\n",mountpoint);
762 if(stat (".", &statbuf)) {
763 printf("mount error: mount point %s does not exist\n",mountpoint);
767 if (S_ISDIR(statbuf.st_mode) == 0) {
768 printf("mount error: mount point %s is not a directory\n",mountpoint);
772 if((getuid() != 0) && (geteuid() == 0)) {
773 if((statbuf.st_uid == getuid()) && (S_IRWXU == (statbuf.st_mode & S_IRWXU))) {
774 #ifndef CIFS_ALLOW_USR_SUID
775 /* Do not allow user mounts to control suid flag
776 for mount unless explicitly built that way */
777 flags |= MS_NOSUID | MS_NODEV;
780 printf("mount error: permission denied or not superuser and mount.cifs not installed SUID\n");
786 user_name = getusername();
788 if(got_password == 0) {
789 mountpassword = getpass("Password: "); /* BB obsolete */
792 /* FIXME launch daemon (handles dfs name resolution and credential change)
793 remember to clear parms and overwrite password field before launching */
795 optlen = strlen(orgoptions);
800 optlen += strlen(share_name) + 4;
802 optlen += strlen(user_name) + 6;
804 optlen += strlen(ipaddr) + 4;
806 optlen += strlen(mountpassword) + 6;
807 options = malloc(optlen + 10);
809 if(options == NULL) {
810 printf("Could not allocate memory for mount options\n");
816 strncat(options,"unc=",4);
817 strcat(options,share_name);
818 /* scan backwards and reverse direction of slash */
819 temp = strrchr(options, '/');
820 if(temp > options + 6)
823 strncat(options,",ip=",4);
824 strcat(options,ipaddr);
827 strncat(options,",user=",6);
828 strcat(options,user_name);
831 strncat(options,",pass=",6);
832 strcat(options,mountpassword);
834 strncat(options,",ver=",5);
835 strcat(options,MOUNT_CIFS_VERSION_MAJOR);
839 strcat(options,orgoptions);
842 printf("\nmount.cifs kernel mount options %s \n",options);
843 if(mount(share_name, mountpoint, "cifs", flags, options)) {
844 /* remember to kill daemon on error */
847 printf("mount failed but no error number set\n");
850 printf("mount error: cifs filesystem not supported by the system\n");
853 printf("mount error %d = %s\n",errno,strerror(errno));
855 printf("Refer to the mount.cifs(8) manual page (e.g.man mount.cifs)\n");
857 memset(mountpassword,0,64);
861 pmntfile = setmntent(MOUNTED, "a+");
863 mountent.mnt_fsname = share_name;
864 mountent.mnt_dir = mountpoint;
865 mountent.mnt_type = "cifs";
866 mountent.mnt_opts = malloc(200);
867 if(mountent.mnt_opts) {
868 memset(mountent.mnt_opts,0,200);
869 if(flags & MS_RDONLY)
870 strcat(mountent.mnt_opts,"ro");
872 strcat(mountent.mnt_opts,"rw");
873 if(flags & MS_MANDLOCK)
874 strcat(mountent.mnt_opts,",mand");
876 strcat(mountent.mnt_opts,",nomand");
877 if(flags & MS_NOEXEC)
878 strcat(mountent.mnt_opts,",noexec");
879 if(flags & MS_NOSUID)
880 strcat(mountent.mnt_opts,",nosuid");
882 strcat(mountent.mnt_opts,",nodev");
883 if(flags & MS_SYNCHRONOUS)
884 strcat(mountent.mnt_opts,",synch");
886 mountent.mnt_freq = 0;
887 mountent.mnt_passno = 0;
888 rc = addmntent(pmntfile,&mountent);
890 if(mountent.mnt_opts)
891 free(mountent.mnt_opts);
893 printf("could not update mount table\n");
897 memset(mountpassword,0,64);
902 memset(options,0,optlen);
907 memset(orgoptions,0,orgoptlen);