r6514: Allow domain= to be specified in credentials file. Fix umount.cifs help, allow...
[samba.git] / source / client / umount.cifs.c
1 /* 
2    Unmount utility program for Linux CIFS VFS (virtual filesystem) client
3    Copyright (C) 2005 Steve French  (sfrench@us.ibm.com)
4
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.
9    
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.
14    
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.  */
18
19 #ifndef _GNU_SOURCE
20 #define _GNU_SOURCE
21 #endif
22
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <unistd.h>
26 #include <ctype.h>
27 #include <sys/types.h>
28 #include <sys/mount.h>
29 #include <sys/ioctl.h>
30 #include <sys/stat.h>
31 #include <sys/vfs.h>
32 #include <fcntl.h>
33 #include <getopt.h>
34 #include <errno.h>
35 #include <string.h>
36 #include <mntent.h>
37 #include <fstab.h>
38
39 #define UNMOUNT_CIFS_VERSION_MAJOR "0"
40 #define UNMOUNT_CIFS_VERSION_MINOR "4"
41
42 #ifndef UNMOUNT_CIFS_VENDOR_SUFFIX
43 #define UNMOUNT_CIFS_VENDOR_SUFFIX ""
44 #endif
45
46 #ifndef MNT_DETACH
47 #define MNT_DETACH 0x02
48 #endif
49
50 #ifndef MNT_EXPIRE
51 #define MNT_EXPIRE 0x04
52 #endif
53
54 #ifndef MOUNTED_LOCK
55 #define MOUNTED_LOCK    "/etc/mtab~"
56 #endif
57 #ifndef MOUNTED_TEMP
58 #define MOUNTED_TEMP    "/etc/mtab.tmp"
59 #endif
60
61 #define CIFS_IOC_CHECKUMOUNT _IO(0xCF, 2)
62 #define CIFS_MAGIC_NUMBER 0xFF534D42   /* the first four bytes of SMB PDU */
63    
64 static struct option longopts[] = {
65         { "all", 0, NULL, 'a' },
66         { "help",0, NULL, 'h' },
67         { "read-only", 0, NULL, 'r' },
68         { "ro", 0, NULL, 'r' },
69         { "verbose", 0, NULL, 'v' },
70         { "version", 0, NULL, 'V' },
71         { "expire", 0, NULL, 'e' },
72         { "force", 0, 0, 'f' },
73         { "lazy", 0, 0, 'l' },
74         { "no-mtab", 0, 0, 'n' },
75         { NULL, 0, NULL, 0 }
76 };
77
78 char * thisprogram;
79 int verboseflg = 0;
80
81 static void umount_cifs_usage(void)
82 {
83         printf("\nUsage:  %s <remotetarget> <dir>\n", thisprogram);
84         printf("\nUnmount the specified directory\n");
85         printf("\nLess commonly used options:");
86         printf("\n\t-r\tIf mount fails, retry with readonly remount.");
87         printf("\n\t-n\tDo not write to mtab.");
88         printf("\n\t-f\tAttempt a forced unmount, even if the fs is busy.");
89         printf("\n\t-l\tAttempt lazy unmount, Unmount now, cleanup later.");
90         printf("\n\t-v\tEnable verbose mode (may be useful for debugging).");
91         printf("\n\t-h\tDisplay this help.");
92         printf("\n\nOptions are described in more detail in the manual page");
93         printf("\n\tman 8 umount.cifs\n");
94         printf("\nTo display the version number of the cifs umount utility:");
95         printf("\n\t%s -V\n",thisprogram);
96         printf("\nNote that invoking the umount utility on cifs mounts, can execute /sbin/umount.cifs (if it is present and -i is not specified to umount).\n");
97 }
98
99 static int umount_check_perm(char * dir)
100 {
101         int fileid;
102         int rc;
103
104         /* allow root to unmount, no matter what */
105         if(getuid() == 0)
106                 return 0;
107
108         /* presumably can not chdir into the target as we do on mount */
109         fileid = open(dir, O_RDONLY | O_DIRECTORY | O_NOFOLLOW, 0);
110         if(fileid == -1) {
111                 if(verboseflg)
112                         printf("error opening mountpoint %d %s",errno,strerror(errno));
113                 return errno;
114         }
115
116         rc = ioctl(fileid, CIFS_IOC_CHECKUMOUNT, NULL);
117
118         if(verboseflg)
119                 printf("ioctl returned %d with errno %d %s\n",rc,errno,strerror(errno));
120
121         if(rc == ENOTTY)
122                 printf("user unmounting via %s is an optional feature of the cifs filesystem driver (cifs.ko)\n\tand requires cifs.ko version 1.32 or later\n",thisprogram);
123         else if (rc > 0)
124                 printf("user unmount of %s failed with %d %s\n",dir,errno,strerror(errno));
125         close(fileid);
126
127         return rc;
128 }
129
130 int remove_from_mtab(char * mountpoint)
131 {
132         int rc = 0;
133         FILE * org_fd;
134         FILE * new_fd;
135         struct mntent * mount_entry;
136
137         /* Do we need to check if it is a symlink to e.g. /proc/mounts
138         in which case we probably do not want to update it? */
139
140         /* Do we first need to check if it is writable? */ 
141
142         /* BB lock_mtab - the smbumount and mount-utils package appear to lock 
143         in a different incompat. way, and it seems that mount-utils have
144         changed the way they lock a few times. We should fix this someday
145         or at least find out how the mount-utils are supposed to handle this */
146
147         if(verboseflg)
148                 printf("attempting to remove from mtab\n");
149
150         org_fd = setmntent(MOUNTED, "r");
151
152         if(org_fd == NULL) {
153                 printf("Can not open %s\n",MOUNTED);
154                 return -EIO;
155         }
156
157         new_fd = setmntent(MOUNTED_TEMP,"w");
158         if(new_fd == NULL) {
159                 printf("Can not open temp file %s", MOUNTED_TEMP);
160                 endmntent(org_fd);
161                 return -EIO;
162         }
163
164         /* BB fix so we only remove the last entry that matches BB */
165         while((mount_entry = getmntent(org_fd)) != NULL) {
166                 if(strcmp(mount_entry->mnt_dir, mountpoint) != 0) {
167                         addmntent(new_fd, mount_entry);
168                 } else {
169                         if(verboseflg)
170                                 printf("entry not copied (removed)\n");
171                 }
172
173         }
174
175         if(verboseflg)
176                 printf("done updating tmp file\n");
177         rc = fchmod (fileno (new_fd), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
178         if(rc < 0) {
179                 printf("error %s changing mode of %s\n", strerror(errno),
180                         MOUNTED_TEMP);
181         }
182         endmntent(new_fd);
183
184         rc = rename(MOUNTED_TEMP, MOUNTED);
185
186         if(rc < 0) {
187                 printf("failure %s renaming %s to %s\n",strerror(errno),
188                         MOUNTED_TEMP, MOUNTED);
189                 return -EIO;
190         }
191
192         /* BB unlock_mtab - unlink(MOUNTED_LOCK) */
193         
194         return rc;
195 }
196
197 int main(int argc, char ** argv)
198 {
199         int c;
200         int rc;
201         int flags = 0;
202         int nomtab = 0;
203         int retry_remount = 0;
204         struct statfs statbuf;
205         char * mountpoint;
206
207         if(argc && argv) {
208                 thisprogram = argv[0];
209         } else {
210                 umount_cifs_usage();
211                 return -EINVAL;
212         }
213
214         if(argc < 2) {
215                 umount_cifs_usage();
216                 return -EINVAL;
217         }
218
219         if(thisprogram == NULL)
220                 thisprogram = "umount.cifs";
221
222         /* add sharename in opts string as unc= parm */
223
224         while ((c = getopt_long (argc, argv, "afhilnrvV",
225                          longopts, NULL)) != -1) {
226                 switch (c) {
227 /* No code to do the following  option yet */
228 /*              case 'a':              
229                         ++umount_all;
230                         break; */
231                 case '?':
232                 case 'h':   /* help */
233                         umount_cifs_usage();
234                         exit(1);
235                 case 'n':
236                         ++nomtab;
237                         break;
238                 case 'f':
239                         flags |= MNT_FORCE;
240                         break;
241                 case 'l':
242                         flags |= MNT_DETACH; /* lazy unmount */
243                         break;
244                 case 'e':
245                         flags |= MNT_EXPIRE; /* gradually timeout */
246                         break;
247                 case 'r':
248                         ++retry_remount;
249                         break;
250                 case 'v':
251                         ++verboseflg;
252                         break;
253                 case 'V':          
254                         printf ("umount.cifs version: %s.%s%s\n",
255                                 UNMOUNT_CIFS_VERSION_MAJOR,
256                                 UNMOUNT_CIFS_VERSION_MINOR,
257                                 UNMOUNT_CIFS_VENDOR_SUFFIX);
258                         exit (0);
259                 default:
260                         printf("unknown unmount option %c\n",c);
261                         umount_cifs_usage();
262                         exit(1);
263                 }
264         }
265
266         /* move past the umount options */
267         argv += optind;
268         argc -= optind;
269
270         mountpoint = argv[0];
271
272         if((argc < 1) || (argv[0] == NULL)) {
273                 printf("\nMissing name of unmount directory\n");
274                 umount_cifs_usage();
275                 return -EINVAL;
276         }
277
278         if(verboseflg)
279                 printf("optind %d unmount dir %s\n",optind, mountpoint);
280
281         /* check if running effectively root */
282         if(geteuid() != 0) {
283                 printf("Trying to unmount when %s not installed suid\n",thisprogram);
284                 if(verboseflg)
285                         printf("euid = %d\n",geteuid());
286                 return -EACCES;
287         }
288
289         /* fixup path if needed */
290
291         /* make sure that this is a cifs filesystem */
292         rc = statfs(mountpoint, &statbuf);
293         
294         if(rc || (statbuf.f_type != CIFS_MAGIC_NUMBER)) {
295                 printf("Wrong filesystem. This utility only unmounts cifs filesystems.\n");
296                 return -EINVAL;
297         }
298
299         /* check if our uid was the one who mounted */
300         rc = umount_check_perm(mountpoint);
301         if (rc) {
302                 printf("Not permitted to unmount\n");
303                 return rc;
304         }
305
306         if(umount2(mountpoint, flags)) {
307         /* remember to kill daemon on error */
308                 switch (errno) {
309                 case 0:
310                         printf("unmount failed but no error number set\n");
311                         break;
312                 default:
313                         printf("unmount error %d = %s\n",errno,strerror(errno));
314                 }
315                 printf("Refer to the umount.cifs(8) manual page (e.g.man 8 umount.cifs)\n");
316                 return -1;
317         } else {
318                 if(verboseflg)
319                         printf("umount2 succeeded\n");
320                 if(nomtab == 0)
321                         remove_from_mtab(mountpoint);
322         }
323
324         return 0;
325 }
326