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 "0"
43 #ifndef MOUNT_CIFS_VENDOR_SUFFIX
44 #define MOUNT_CIFS_VENDOR_SUFFIX ""
49 static int got_password
= 0;
50 static int got_user
= 0;
51 static int got_domain
= 0;
52 static int got_ip
= 0;
53 static int got_unc
= 0;
54 static int got_uid
= 0;
55 static int got_gid
= 0;
56 static char * user_name
= NULL
;
57 char * mountpassword
= NULL
;
63 open nofollow - avoid symlink exposure?
64 get owner of dir see if matches self or if root
65 call system(umount argv) etc.
69 static void mount_cifs_usage(void)
71 printf("\nUsage: %s <remotetarget> <dir> -o <options>\n", thisprogram
);
72 printf("\nMount the remote target, specified as a UNC name,");
73 printf(" to a local directory.\n");
75 memset(mountpassword
,0,64);
81 /* caller frees username if necessary */
82 static char * getusername(void) {
83 char *username
= NULL
;
84 struct passwd
*password
= getpwuid(getuid());
87 username
= password
->pw_name
;
92 char * parse_cifs_url(char * unc_name
)
94 printf("\ncifs url %s\n",unc_name
);
98 static int open_cred_file(char * file_name
)
104 fs
= fopen(file_name
,"r");
107 line_buf
= malloc(4096);
111 while(fgets(line_buf
,4096,fs
)) {
112 /* parse line from credential file */
114 /* eat leading white space */
115 for(i
=0;i
<4096;i
++) {
116 if(line_buf
[i
] == '\0')
118 else if((line_buf
[i
] != ' ') && (line_buf
[i
] != '\t'))
123 if (strncasecmp("username",line_buf
,8) == 0) {
124 temp_val
= strchr(line_buf
+ i
,'=');
126 /* go past equals sign */
128 length
= strlen(temp_val
);
130 printf("cifs.mount failed due to malformed username in credentials file");
131 memset(line_buf
,0,4096);
133 memset(mountpassword
,0,64);
138 user_name
= calloc(1 + length
,1);
139 /* BB adding free of user_name string before exit,
140 not really necessary but would be cleaner */
141 strncpy(user_name
,temp_val
, length
);
144 } else if (strncasecmp("password",line_buf
,8) == 0) {
145 temp_val
= strchr(line_buf
+i
,'=');
147 /* go past equals sign */
149 length
= strlen(temp_val
);
151 printf("cifs.mount failed: password in credentials file too long\n");
152 memset(line_buf
,0, 4096);
154 memset(mountpassword
,0,64);
158 if(mountpassword
== NULL
) {
159 mountpassword
= calloc(65,1);
162 strncpy(mountpassword
,temp_val
,64);
171 memset(line_buf
,0,4096);
177 static int get_password_from_file(int file_descript
, char * filename
)
183 if(mountpassword
== NULL
)
184 mountpassword
= calloc(65,1);
186 memset(mountpassword
, 0, 64);
188 if(filename
!= NULL
) {
189 file_descript
= open(filename
, O_RDONLY
);
190 if(file_descript
< 0) {
191 printf("cifs.mount failed. %s attempting to open password file %s\n",
192 strerror(errno
),filename
);
196 /* else file already open and fd provided */
199 rc
= read(file_descript
,&c
,1);
201 printf("cifs.mount failed. Error %s reading password file\n",strerror(errno
));
202 memset(mountpassword
,0,64);
204 close(file_descript
);
207 if(mountpassword
[0] == 0) {
209 printf("\nWarning: null password used since cifs password file empty");
212 } else /* read valid character */ {
213 if((c
== 0) || (c
== '\n')) {
216 mountpassword
[i
] = c
;
219 if((i
== 64) && (verboseflag
)) {
220 printf("\nWarning: password longer than 64 characters specified in cifs password file");
223 if(filename
!= NULL
) {
224 close(file_descript
);
230 static int parse_options(char * options
)
233 char * percent_char
= 0;
235 char * next_keyword
= 0;
244 printf("\n parsing options: %s", options
);
246 /* while ((data = strsep(&options, ",")) != NULL) { */
247 while(data
!= NULL
) {
248 /* check if ends with trailing comma */
252 /* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
253 /* data = next keyword */
254 /* value = next value ie stuff after equal sign */
256 next_keyword
= strchr(data
,',');
258 /* temporarily null terminate end of keyword=value pair */
265 /* temporarily null terminate keyword to make keyword and value distinct */
266 if ((value
= strchr(data
, '=')) != NULL
) {
271 if (strncmp(data
, "user", 4) == 0) {
272 if (!value
|| !*value
) {
273 printf("invalid or missing username\n");
274 return 1; /* needs_arg; */
276 if (strnlen(value
, 260) < 260) {
278 percent_char
= strchr(value
,'%');
281 if(mountpassword
== NULL
)
282 mountpassword
= calloc(65,1);
285 printf("\ncifs.mount warning - password specified twice\n");
288 strncpy(mountpassword
, percent_char
,64);
289 /* remove password from username */
290 while(*percent_char
!= 0) {
297 printf("username too long\n");
300 } else if (strncmp(data
, "pass", 4) == 0) {
301 if (!value
|| !*value
) {
303 printf("\npassword specified twice, ignoring second\n");
306 } else if (strnlen(value
, 17) < 17) {
308 printf("\ncifs.mount warning - password specified twice\n");
311 printf("password too long\n");
314 } else if (strncmp(data
, "ip", 2) == 0) {
315 if (!value
|| !*value
) {
316 printf("target ip address argument missing");
317 } else if (strnlen(value
, 35) < 35) {
320 printf("ip address too long\n");
323 } else if ((strncmp(data
, "unc", 3) == 0)
324 || (strncmp(data
, "target", 6) == 0)
325 || (strncmp(data
, "path", 4) == 0)) {
326 if (!value
|| !*value
) {
327 printf("invalid path to network resource\n");
328 return 1; /* needs_arg; */
329 } else if(strnlen(value
,5) < 5) {
330 printf("UNC name too short");
333 if (strnlen(value
, 300) < 300) {
335 if (strncmp(value
, "//", 2) == 0) {
337 printf("unc name specified twice, ignoring second\n");
340 } else if (strncmp(value
, "\\\\", 2) != 0) {
341 printf("UNC Path does not begin with // or \\\\ \n");
345 printf("unc name specified twice, ignoring second\n");
350 printf("CIFS: UNC name too long\n");
353 } else if ((strncmp(data
, "domain", 3) == 0)
354 || (strncmp(data
, "workgroup", 5) == 0)) {
355 if (!value
|| !*value
) {
356 printf("CIFS: invalid domain name\n");
357 return 1; /* needs_arg; */
359 if (strnlen(value
, 65) < 65) {
362 printf("domain name too long\n");
365 } else if (strncmp(data
, "cred", 4) == 0) {
366 if (value
&& *value
) {
367 rc
= open_cred_file(value
);
369 printf("error %d opening credential file %s",rc
, value
);
373 printf("invalid credential file name specified\n");
376 } else if (strncmp(data
, "uid", 3) == 0) {
377 if (value
&& *value
) {
380 } else if (strncmp(data
, "gid", 3) == 0) {
381 if (value
&& *value
) {
384 /* fmask and dmask synonyms for people used to smbfs syntax */
385 } else if (strcmp(data
, "file_mode") == 0 || strcmp(data
, "fmask")==0) {
386 if (!value
|| !*value
) {
387 printf ("Option '%s' requires a numerical argument\n", data
);
391 if (value
[0] != '0') {
392 printf ("WARNING: '%s' not expressed in octal.\n", data
);
395 if (strcmp (data
, "fmask") == 0) {
396 printf ("WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
399 } else if (strcmp(data
, "dir_mode") == 0 || strcmp(data
, "dmask")==0) {
400 if (!value
|| !*value
) {
401 printf ("Option '%s' requires a numerical argument\n", data
);
405 if (value
[0] != '0') {
406 printf ("WARNING: '%s' not expressed in octal.\n", data
);
409 if (strcmp (data
, "dmask") == 0) {
410 printf ("WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
413 } /* else if (strnicmp(data, "port", 4) == 0) {
414 if (value && *value) {
416 simple_strtoul(value, &value, 0);
418 } else if (strnicmp(data, "rsize", 5) == 0) {
419 if (value && *value) {
421 simple_strtoul(value, &value, 0);
423 } else if (strnicmp(data, "wsize", 5) == 0) {
424 if (value && *value) {
426 simple_strtoul(value, &value, 0);
428 } else if (strnicmp(data, "version", 3) == 0) {
430 } else if (strnicmp(data, "rw", 2) == 0) {
433 printf("CIFS: Unknown mount option %s\n",data); */
435 /* move to next option */
436 data
= next_keyword
+1;
438 /* put overwritten equals sign back */
444 /* put previous overwritten comma back */
454 /* Note that caller frees the returned buffer if necessary */
455 char * parse_server(char * unc_name
)
457 int length
= strnlen(unc_name
,1024);
459 char * ipaddress_string
= NULL
;
460 struct hostent
* host_entry
;
461 struct in_addr server_ipaddr
;
465 printf("mount error: UNC name too long");
468 if (strncasecmp("cifs://",unc_name
,7) == 0)
469 return parse_cifs_url(unc_name
+7);
470 if (strncasecmp("smb://",unc_name
,6) == 0) {
471 return parse_cifs_url(unc_name
+6);
475 /* BB add code to find DFS root here */
476 printf("\nMounting the DFS root for domain not implemented yet");
479 if(strncmp(unc_name
,"//",2) && strncmp(unc_name
,"\\\\",2)) {
480 printf("mount error: improperly formatted UNC name.");
481 printf(" %s does not begin with \\\\ or //\n",unc_name
);
488 if ((share
= strchr(unc_name
, '/')) ||
489 (share
= strchr(unc_name
,'\\'))) {
490 *share
= 0; /* temporarily terminate the string */
492 host_entry
= gethostbyname(unc_name
);
493 *(share
- 1) = '/'; /* put the slash back */
494 /* rc = getipnodebyname(unc_name, AF_INET, AT_ADDRCONFIG ,&rc);*/
495 if(host_entry
== NULL
) {
496 printf("mount error: could not find target server. TCP name %s not found ", unc_name
);
497 printf(" rc = %d\n",rc
);
501 /* BB should we pass an alternate version of the share name as Unicode */
502 /* BB what about ipv6? BB */
503 /* BB add retries with alternate servers in list */
505 memcpy(&server_ipaddr
.s_addr
, host_entry
->h_addr
, 4);
507 ipaddress_string
= inet_ntoa(server_ipaddr
);
508 if(ipaddress_string
== NULL
) {
509 printf("mount error: could not get valid ip address for target server\n");
512 return ipaddress_string
;
515 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
516 printf("Mounting the DFS root for a particular server not implemented yet\n");
523 static struct option longopts
[] = {
524 { "all", 0, 0, 'a' },
525 { "help", 0, 0, 'h' },
526 { "read-only", 0, 0, 'r' },
528 { "verbose", 0, 0, 'v' },
529 { "version", 0, 0, 'V' },
530 { "read-write", 0, 0, 'w' },
532 { "options", 1, 0, 'o' },
533 { "types", 1, 0, 't' },
534 { "rsize",1, 0, 'R' },
535 { "wsize",1, 0, 'W' },
540 { "username",1,0,'u'},
543 { "password",1,0,'p'},
545 { "credentials",1,0,'c'},
550 int main(int argc
, char ** argv
)
553 int flags
= MS_MANDLOCK
| MS_MGC_VAL
;
554 char * orgoptions
= NULL
;
555 char * share_name
= NULL
;
556 char * domain_name
= NULL
;
557 char * ipaddr
= NULL
;
571 struct utsname sysinfo
;
572 struct mntent mountent
;
575 /* setlocale(LC_ALL, "");
576 bindtextdomain(PACKAGE, LOCALEDIR);
577 textdomain(PACKAGE); */
580 thisprogram
= argv
[0];
582 if(thisprogram
== NULL
)
583 thisprogram
= "mount.cifs";
586 /* BB add workstation name and domain and pass down */
588 /* #ifdef _GNU_SOURCE
589 printf(" node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
592 share_name
= argv
[1];
593 mountpoint
= argv
[2];
595 /* add sharename in opts string as unc= parm */
597 while ((c
= getopt_long (argc
, argv
, "afFhilL:no:O:rsU:vVwt:",
598 longopts
, NULL
)) != -1) {
600 /* No code to do the following options yet */
602 list_with_volumelabel = 1;
605 volumelabel = optarg;
619 orgoptions
= strdup(optarg
);
621 case 'r': /* mount readonly */
631 printf ("mount.cifs version: %s.%s%s\n",
632 MOUNT_CIFS_VERSION_MAJOR
,
633 MOUNT_CIFS_VERSION_MINOR
,
634 MOUNT_CIFS_VENDOR_SUFFIX
);
636 memset(mountpassword
,0,64);
643 rsize
= atoi(optarg
) ;
646 wsize
= atoi(optarg
);
659 domain_name
= optarg
;
662 if(mountpassword
== NULL
)
663 mountpassword
= calloc(65,1);
666 strncpy(mountpassword
,optarg
,64);
672 printf("unknown mount option %c\n",c
);
681 if (getenv("PASSWD")) {
682 if(mountpassword
== NULL
)
683 mountpassword
= calloc(65,1);
685 strncpy(mountpassword
,getenv("PASSWD"),64);
688 } else if (getenv("PASSWD_FD")) {
689 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL
);
690 } else if (getenv("PASSWD_FILE")) {
691 get_password_from_file(0, getenv("PASSWD_FILE"));
694 ipaddr
= parse_server(share_name
);
696 if (orgoptions
&& parse_options(orgoptions
))
699 /* BB save off path and pop after mount returns? */
700 /* BB canonicalize the path in argv[1]? */
702 if(chdir(mountpoint
)) {
703 printf("mount error: can not change directory into mount target %s\n",mountpoint
);
706 if(stat (mountpoint
, &statbuf
)) {
707 printf("mount error: mount point %s does not exist\n",mountpoint
);
711 if (S_ISDIR(statbuf
.st_mode
) == 0) {
712 printf("mount error: mount point %s is not a directory\n",mountpoint
);
716 if((getuid() != 0) && (geteuid() == 0)) {
717 if((statbuf
.st_uid
== getuid()) && (S_IRWXU
== (statbuf
.st_mode
& S_IRWXU
))) {
718 printf("setuid mount allowed\n");
720 printf("mount error: permission denied or not superuser and cifs.mount not installed SUID\n");
726 user_name
= getusername();
728 if(got_password
== 0) {
729 mountpassword
= getpass("Password: "); /* BB obsolete */
732 /* FIXME launch daemon (handles dfs name resolution and credential change)
733 remember to clear parms and overwrite password field before launching */
735 optlen
= strlen(orgoptions
);
740 optlen
+= strlen(share_name
) + 4;
742 optlen
+= strlen(user_name
) + 6;
744 optlen
+= strlen(ipaddr
) + 4;
746 optlen
+= strlen(mountpassword
) + 6;
747 options
= malloc(optlen
+ 10);
750 strncat(options
,"unc=",4);
751 strcat(options
,share_name
);
752 /* scan backwards and reverse direction of slash */
753 temp
= strrchr(options
, '/');
754 if(temp
> options
+ 6)
757 strncat(options
,",ip=",4);
758 strcat(options
,ipaddr
);
761 strncat(options
,",user=",6);
762 strcat(options
,user_name
);
765 strncat(options
,",pass=",6);
766 strcat(options
,mountpassword
);
768 strncat(options
,",ver=",5);
769 strcat(options
,MOUNT_CIFS_VERSION_MAJOR
);
773 strcat(options
,orgoptions
);
776 printf("\ncifs.mount kernel mount options %s \n",options
);
777 if(mount(share_name
, mountpoint
, "cifs", flags
, options
)) {
778 /* remember to kill daemon on error */
781 printf("mount failed but no error number set\n");
784 printf("mount error: cifs filesystem not supported by the system\n");
787 printf("mount error %d = %s\n",errno
,strerror(errno
));
789 printf("Refer to the mount.cifs(8) manual page (e.g.man mount.cifs)\n");
791 memset(mountpassword
,0,64);
795 pmntfile
= setmntent(MOUNTED
, "a+");
797 mountent
.mnt_fsname
= share_name
;
798 mountent
.mnt_dir
= mountpoint
;
799 mountent
.mnt_type
= "cifs";
800 mountent
.mnt_opts
= "";
801 mountent
.mnt_freq
= 0;
802 mountent
.mnt_passno
= 0;
803 rc
= addmntent(pmntfile
,&mountent
);
806 printf("could not update mount table\n");
810 memset(mountpassword
,0,64);
815 memset(options
,0,optlen
);
820 memset(orgoptions
,0,orgoptlen
);