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");
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
<4086;i
++) {
120 if((line_buf
[i
] != ' ') && (line_buf
[i
] != '\t'))
122 /* if whitespace - skip past it */
124 if (strncasecmp("username",line_buf
+i
,8) == 0) {
125 temp_val
= strchr(line_buf
+ i
,'=');
127 /* go past equals sign */
129 for(length
= 0;length
<4087;length
++) {
130 if(temp_val
[length
] == '\n')
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
+i
,8) == 0) {
149 temp_val
= strchr(line_buf
+i
,'=');
151 /* go past equals sign */
153 for(length
= 0;length
<65;length
++) {
154 if(temp_val
[length
] == '\n')
158 printf("cifs.mount failed: password in credentials file too long\n");
159 memset(line_buf
,0, 4096);
161 memset(mountpassword
,0,64);
165 if(mountpassword
== NULL
) {
166 mountpassword
= calloc(65,1);
168 memset(mountpassword
,0,64);
170 /* BB add handling for commas in password here */
171 strncpy(mountpassword
,temp_val
,length
);
180 memset(line_buf
,0,4096);
186 static int get_password_from_file(int file_descript
, char * filename
)
192 if(mountpassword
== NULL
)
193 mountpassword
= calloc(65,1);
195 memset(mountpassword
, 0, 64);
197 if(filename
!= NULL
) {
198 file_descript
= open(filename
, O_RDONLY
);
199 if(file_descript
< 0) {
200 printf("cifs.mount failed. %s attempting to open password file %s\n",
201 strerror(errno
),filename
);
205 /* else file already open and fd provided */
208 rc
= read(file_descript
,&c
,1);
210 printf("cifs.mount failed. Error %s reading password file\n",strerror(errno
));
211 memset(mountpassword
,0,64);
213 close(file_descript
);
216 if(mountpassword
[0] == 0) {
218 printf("\nWarning: null password used since cifs password file empty");
221 } else /* read valid character */ {
222 if((c
== 0) || (c
== '\n')) {
225 mountpassword
[i
] = c
;
228 if((i
== 64) && (verboseflag
)) {
229 printf("\nWarning: password longer than 64 characters specified in cifs password file");
232 if(filename
!= NULL
) {
233 close(file_descript
);
239 static int parse_options(char * options
, int * filesys_flags
)
242 char * percent_char
= 0;
244 char * next_keyword
= 0;
253 printf("\n parsing options: %s", options
);
255 /* while ((data = strsep(&options, ",")) != NULL) { */
256 while(data
!= NULL
) {
257 /* check if ends with trailing comma */
261 /* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
262 /* data = next keyword */
263 /* value = next value ie stuff after equal sign */
265 next_keyword
= strchr(data
,',');
267 /* temporarily null terminate end of keyword=value pair */
274 /* temporarily null terminate keyword to make keyword and value distinct */
275 if ((value
= strchr(data
, '=')) != NULL
) {
280 if (strncmp(data
, "user", 4) == 0) {
281 if (!value
|| !*value
) {
282 printf("invalid or missing username\n");
283 return 1; /* needs_arg; */
285 if (strnlen(value
, 260) < 260) {
287 percent_char
= strchr(value
,'%');
290 if(mountpassword
== NULL
)
291 mountpassword
= calloc(65,1);
294 printf("\ncifs.mount warning - password specified twice\n");
297 strncpy(mountpassword
, percent_char
,64);
298 /* remove password from username */
299 while(*percent_char
!= 0) {
306 printf("username too long\n");
309 } else if (strncmp(data
, "pass", 4) == 0) {
310 if (!value
|| !*value
) {
312 printf("\npassword specified twice, ignoring second\n");
315 } else if (strnlen(value
, 17) < 17) {
317 printf("\ncifs.mount warning - password specified twice\n");
320 printf("password too long\n");
323 } else if (strncmp(data
, "ip", 2) == 0) {
324 if (!value
|| !*value
) {
325 printf("target ip address argument missing");
326 } else if (strnlen(value
, 35) < 35) {
329 printf("ip address too long\n");
332 } else if ((strncmp(data
, "unc", 3) == 0)
333 || (strncmp(data
, "target", 6) == 0)
334 || (strncmp(data
, "path", 4) == 0)) {
335 if (!value
|| !*value
) {
336 printf("invalid path to network resource\n");
337 return 1; /* needs_arg; */
338 } else if(strnlen(value
,5) < 5) {
339 printf("UNC name too short");
342 if (strnlen(value
, 300) < 300) {
344 if (strncmp(value
, "//", 2) == 0) {
346 printf("unc name specified twice, ignoring second\n");
349 } else if (strncmp(value
, "\\\\", 2) != 0) {
350 printf("UNC Path does not begin with // or \\\\ \n");
354 printf("unc name specified twice, ignoring second\n");
359 printf("CIFS: UNC name too long\n");
362 } else if ((strncmp(data
, "domain", 3) == 0)
363 || (strncmp(data
, "workgroup", 5) == 0)) {
364 if (!value
|| !*value
) {
365 printf("CIFS: invalid domain name\n");
366 return 1; /* needs_arg; */
368 if (strnlen(value
, 65) < 65) {
371 printf("domain name too long\n");
374 } else if (strncmp(data
, "cred", 4) == 0) {
375 if (value
&& *value
) {
376 rc
= open_cred_file(value
);
378 printf("error %d opening credential file %s\n",rc
, value
);
382 printf("invalid credential file name specified\n");
385 } else if (strncmp(data
, "uid", 3) == 0) {
386 if (value
&& *value
) {
389 } else if (strncmp(data
, "gid", 3) == 0) {
390 if (value
&& *value
) {
393 /* fmask and dmask synonyms for people used to smbfs syntax */
394 } else if (strcmp(data
, "file_mode") == 0 || strcmp(data
, "fmask")==0) {
395 if (!value
|| !*value
) {
396 printf ("Option '%s' requires a numerical argument\n", data
);
400 if (value
[0] != '0') {
401 printf ("WARNING: '%s' not expressed in octal.\n", data
);
404 if (strcmp (data
, "fmask") == 0) {
405 printf ("WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
406 data
= "file_mode"; /* BB fix this */
408 } else if (strcmp(data
, "dir_mode") == 0 || strcmp(data
, "dmask")==0) {
409 if (!value
|| !*value
) {
410 printf ("Option '%s' requires a numerical argument\n", data
);
414 if (value
[0] != '0') {
415 printf ("WARNING: '%s' not expressed in octal.\n", data
);
418 if (strcmp (data
, "dmask") == 0) {
419 printf ("WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
422 /* the following eight mount options should be
423 stripped out from what is passed into the kernel
424 since these eight options are best passed as the
425 mount flags rather than redundantly to the kernel
426 and could generate spurious warnings depending on the
427 level of the corresponding cifs vfs kernel code */
428 } else if (strncmp(data
, "nosuid", 6) == 0) {
429 *filesys_flags
|= MS_NOSUID
;
430 } else if (strncmp(data
, "suid", 4) == 0) {
431 *filesys_flags
&= ~MS_NOSUID
;
432 } else if (strncmp(data
, "nodev", 5) == 0) {
433 *filesys_flags
|= MS_NODEV
;
434 } else if (strncmp(data
, "dev", 3) == 0) {
435 *filesys_flags
&= ~MS_NODEV
;
436 } else if (strncmp(data
, "noexec", 6) == 0) {
437 *filesys_flags
|= MS_NOEXEC
;
438 } else if (strncmp(data
, "exec", 4) == 0) {
439 *filesys_flags
&= ~MS_NOEXEC
;
440 } else if (strncmp(data
, "ro", 2) == 0) {
441 *filesys_flags
|= MS_RDONLY
;
442 } else if (strncmp(data
, "rw", 2) == 0) {
443 *filesys_flags
&= ~MS_RDONLY
;
444 } /* else if (strnicmp(data, "port", 4) == 0) {
445 if (value && *value) {
447 simple_strtoul(value, &value, 0);
449 } else if (strnicmp(data, "rsize", 5) == 0) {
450 if (value && *value) {
452 simple_strtoul(value, &value, 0);
454 } else if (strnicmp(data, "wsize", 5) == 0) {
455 if (value && *value) {
457 simple_strtoul(value, &value, 0);
459 } else if (strnicmp(data, "version", 3) == 0) {
461 printf("CIFS: Unknown mount option %s\n",data);
462 } */ /* nothing to do on those four mount options above.
463 Just pass to kernel and ignore them here */
465 /* move to next option */
466 data
= next_keyword
+1;
468 /* put overwritten equals sign back */
474 /* put previous overwritten comma back */
484 /* Note that caller frees the returned buffer if necessary */
485 char * parse_server(char * unc_name
)
487 int length
= strnlen(unc_name
,1024);
489 char * ipaddress_string
= NULL
;
490 struct hostent
* host_entry
;
491 struct in_addr server_ipaddr
;
495 printf("mount error: UNC name too long");
498 if (strncasecmp("cifs://",unc_name
,7) == 0)
499 return parse_cifs_url(unc_name
+7);
500 if (strncasecmp("smb://",unc_name
,6) == 0) {
501 return parse_cifs_url(unc_name
+6);
505 /* BB add code to find DFS root here */
506 printf("\nMounting the DFS root for domain not implemented yet");
509 if(strncmp(unc_name
,"//",2) && strncmp(unc_name
,"\\\\",2)) {
510 printf("mount error: improperly formatted UNC name.");
511 printf(" %s does not begin with \\\\ or //\n",unc_name
);
518 if ((share
= strchr(unc_name
, '/')) ||
519 (share
= strchr(unc_name
,'\\'))) {
520 *share
= 0; /* temporarily terminate the string */
522 host_entry
= gethostbyname(unc_name
);
523 *(share
- 1) = '/'; /* put the slash back */
524 /* rc = getipnodebyname(unc_name, AF_INET, AT_ADDRCONFIG ,&rc);*/
525 if(host_entry
== NULL
) {
526 printf("mount error: could not find target server. TCP name %s not found ", unc_name
);
527 printf(" rc = %d\n",rc
);
531 /* BB should we pass an alternate version of the share name as Unicode */
532 /* BB what about ipv6? BB */
533 /* BB add retries with alternate servers in list */
535 memcpy(&server_ipaddr
.s_addr
, host_entry
->h_addr
, 4);
537 ipaddress_string
= inet_ntoa(server_ipaddr
);
538 if(ipaddress_string
== NULL
) {
539 printf("mount error: could not get valid ip address for target server\n");
542 return ipaddress_string
;
545 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
546 printf("Mounting the DFS root for a particular server not implemented yet\n");
553 static struct option longopts
[] = {
554 { "all", 0, 0, 'a' },
555 { "help",0, 0, 'h' },
556 { "move",0, 0, 'm' },
557 { "bind",0, 0, 'b' },
558 { "read-only", 0, 0, 'r' },
560 { "verbose", 0, 0, 'v' },
561 { "version", 0, 0, 'V' },
562 { "read-write", 0, 0, 'w' },
564 { "options", 1, 0, 'o' },
565 { "type", 1, 0, 't' },
566 { "rsize",1, 0, 'R' },
567 { "wsize",1, 0, 'W' },
571 { "username",1,0,'u'},
574 { "password",1,0,'p'},
576 { "credentials",1,0,'c'},
578 /* { "uuid",1,0,'U'}, */ /* BB unimplemented */
582 int main(int argc
, char ** argv
)
585 int flags
= MS_MANDLOCK
; /* no need to set legacy MS_MGC_VAL */
586 char * orgoptions
= NULL
;
587 char * share_name
= NULL
;
588 char * domain_name
= NULL
;
589 char * ipaddr
= NULL
;
603 struct utsname sysinfo
;
604 struct mntent mountent
;
607 /* setlocale(LC_ALL, "");
608 bindtextdomain(PACKAGE, LOCALEDIR);
609 textdomain(PACKAGE); */
612 thisprogram
= argv
[0];
614 if(thisprogram
== NULL
)
615 thisprogram
= "mount.cifs";
618 /* BB add workstation name and domain and pass down */
620 /* #ifdef _GNU_SOURCE
621 printf(" node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
624 share_name
= argv
[1];
625 mountpoint
= argv
[2];
627 /* add sharename in opts string as unc= parm */
629 while ((c
= getopt_long (argc
, argv
, "afFhilL:no:O:rsU:vVwt:",
630 longopts
, NULL
)) != -1) {
632 /* No code to do the following options yet */
634 list_with_volumelabel = 1;
637 volumelabel = optarg;
657 orgoptions
= strdup(optarg
);
659 case 'r': /* mount readonly */
669 printf ("mount.cifs version: %s.%s%s\n",
670 MOUNT_CIFS_VERSION_MAJOR
,
671 MOUNT_CIFS_VERSION_MINOR
,
672 MOUNT_CIFS_VENDOR_SUFFIX
);
674 memset(mountpassword
,0,64);
681 rsize
= atoi(optarg
) ;
684 wsize
= atoi(optarg
);
697 domain_name
= optarg
;
700 if(mountpassword
== NULL
)
701 mountpassword
= calloc(65,1);
704 strncpy(mountpassword
,optarg
,64);
710 printf("unknown mount option %c\n",c
);
719 if (getenv("PASSWD")) {
720 if(mountpassword
== NULL
)
721 mountpassword
= calloc(65,1);
723 strncpy(mountpassword
,getenv("PASSWD"),64);
726 } else if (getenv("PASSWD_FD")) {
727 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL
);
728 } else if (getenv("PASSWD_FILE")) {
729 get_password_from_file(0, getenv("PASSWD_FILE"));
732 ipaddr
= parse_server(share_name
);
734 if (orgoptions
&& parse_options(orgoptions
, &flags
))
737 /* BB save off path and pop after mount returns? */
738 /* BB canonicalize the path in argv[1]? */
740 if(chdir(mountpoint
)) {
741 printf("mount error: can not change directory into mount target %s\n",mountpoint
);
745 if(stat (".", &statbuf
)) {
746 printf("mount error: mount point %s does not exist\n",mountpoint
);
750 if (S_ISDIR(statbuf
.st_mode
) == 0) {
751 printf("mount error: mount point %s is not a directory\n",mountpoint
);
755 if((getuid() != 0) && (geteuid() == 0)) {
756 if((statbuf
.st_uid
== getuid()) && (S_IRWXU
== (statbuf
.st_mode
& S_IRWXU
))) {
757 #ifndef CIFS_ALLOW_USR_SUID
758 /* Do not allow user mounts to control suid flag
759 for mount unless explicitly built that way */
760 flags
|= MS_NOSUID
| MS_NODEV
;
763 printf("mount error: permission denied or not superuser and cifs.mount not installed SUID\n");
769 user_name
= getusername();
771 if(got_password
== 0) {
772 mountpassword
= getpass("Password: "); /* BB obsolete */
775 /* FIXME launch daemon (handles dfs name resolution and credential change)
776 remember to clear parms and overwrite password field before launching */
778 optlen
= strlen(orgoptions
);
783 optlen
+= strlen(share_name
) + 4;
785 optlen
+= strlen(user_name
) + 6;
787 optlen
+= strlen(ipaddr
) + 4;
789 optlen
+= strlen(mountpassword
) + 6;
790 options
= malloc(optlen
+ 10);
792 if(options
== NULL
) {
793 printf("Could not allocate memory for mount options\n");
799 strncat(options
,"unc=",4);
800 strcat(options
,share_name
);
801 /* scan backwards and reverse direction of slash */
802 temp
= strrchr(options
, '/');
803 if(temp
> options
+ 6)
806 strncat(options
,",ip=",4);
807 strcat(options
,ipaddr
);
810 strncat(options
,",user=",6);
811 strcat(options
,user_name
);
814 strncat(options
,",pass=",6);
815 strcat(options
,mountpassword
);
817 strncat(options
,",ver=",5);
818 strcat(options
,MOUNT_CIFS_VERSION_MAJOR
);
822 strcat(options
,orgoptions
);
825 printf("\ncifs.mount kernel mount options %s \n",options
);
826 if(mount(share_name
, mountpoint
, "cifs", flags
, options
)) {
827 /* remember to kill daemon on error */
830 printf("mount failed but no error number set\n");
833 printf("mount error: cifs filesystem not supported by the system\n");
836 printf("mount error %d = %s\n",errno
,strerror(errno
));
838 printf("Refer to the mount.cifs(8) manual page (e.g.man mount.cifs)\n");
840 memset(mountpassword
,0,64);
844 pmntfile
= setmntent(MOUNTED
, "a+");
846 mountent
.mnt_fsname
= share_name
;
847 mountent
.mnt_dir
= mountpoint
;
848 mountent
.mnt_type
= "cifs";
849 mountent
.mnt_opts
= malloc(200);
850 if(mountent
.mnt_opts
) {
851 memset(mountent
.mnt_opts
,0,200);
852 if(flags
& MS_RDONLY
)
853 strcat(mountent
.mnt_opts
,"ro");
855 strcat(mountent
.mnt_opts
,"rw");
856 if(flags
& MS_MANDLOCK
)
857 strcat(mountent
.mnt_opts
,",mand");
859 strcat(mountent
.mnt_opts
,",nomand");
860 if(flags
& MS_NOEXEC
)
861 strcat(mountent
.mnt_opts
,",noexec");
862 if(flags
& MS_NOSUID
)
863 strcat(mountent
.mnt_opts
,",nosuid");
865 strcat(mountent
.mnt_opts
,",nodev");
866 if(flags
& MS_SYNCHRONOUS
)
867 strcat(mountent
.mnt_opts
,",synch");
869 mountent
.mnt_freq
= 0;
870 mountent
.mnt_passno
= 0;
871 rc
= addmntent(pmntfile
,&mountent
);
873 if(mountent
.mnt_opts
)
874 free(mountent
.mnt_opts
);
876 printf("could not update mount table\n");
880 memset(mountpassword
,0,64);
885 memset(options
,0,optlen
);
890 memset(orgoptions
,0,orgoptlen
);