r6349: Update cifs umount utility
[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
38 #define UNMOUNT_CIFS_VERSION_MAJOR "0"
39 #define UNMOUNT_CIFS_VERSION_MINOR "2"
40
41 #ifndef UNMOUNT_CIFS_VENDOR_SUFFIX
42 #define UNMOUNT_CIFS_VENDOR_SUFFIX ""
43 #endif
44
45 #ifndef MNT_DETACH
46 #define MNT_DETACH 0x02
47 #endif
48
49 #ifndef MNT_EXPIRE
50 #define MNT_EXPIRE 0x04
51 #endif
52
53 #define CIFS_IOC_CHECKUMOUNT _IO(0xCF, 2)
54 #define CIFS_MAGIC_NUMBER 0xFF534D42   /* the first four bytes of SMB PDU */
55    
56 static struct option longopts[] = {
57         { "all", 0, NULL, 'a' },
58         { "help",0, NULL, 'h' },
59         { "read-only", 0, NULL, 'r' },
60         { "ro", 0, NULL, 'r' },
61         { "verbose", 0, NULL, 'v' },
62         { "version", 0, NULL, 'V' },
63         { "expire", 0, NULL, 'e' },
64         { "force", 0, 0, 'f' },
65         { "lazy", 0, 0, 'l' },
66         { "no-mtab", 0, 0, 'n' },
67         { NULL, 0, NULL, 0 }
68 };
69
70 char * thisprogram;
71 int verboseflg = 0;
72
73 static void umount_cifs_usage(void)
74 {
75         printf("\nUsage:  %s <remotetarget> <dir>\n", thisprogram);
76         printf("\nUnmount the specified directory\n");
77         printf("\nLess commonly used options:");
78         printf("\n\t-r\tIf mount fails, retry with readonly remount.");
79         printf("\n\t-n\tDo not write to mtab.");
80         printf("\n\t-f\tAttempt a forced unmount, even if the fs is busy.");
81         printf("\n\t-l\tAttempt lazy unmount, Unmount now, cleanup later.");
82         printf("\n\t-v\tEnable verbose mode (may be useful for debugging).");
83         printf("\n\t-h\tDisplay this help.");
84         printf("\n\nOptions are described in more detail in the manual page");
85         printf("\n\tman 8 umount.cifs\n");
86         printf("\nTo display the version number of the cifs umount utility:");
87         printf("\n\t%s -V\n",thisprogram);
88 }
89
90 static int umount_check_perm(char * dir)
91 {
92         int fileid;
93         int rc;
94
95         /* presumably can not chdir into the target as we do on mount */
96
97         fileid = open(dir, O_RDONLY | O_DIRECTORY | O_NOFOLLOW, 0);
98         if(fileid == -1) {
99                 if(verboseflg)
100                         printf("error opening mountpoint %d %s",errno,strerror(errno));
101                 return errno;
102         }
103
104         rc = ioctl(fileid, CIFS_IOC_CHECKUMOUNT, NULL);
105
106         if(verboseflg)
107                 printf("ioctl returned %d with errno %d %s\n",rc,errno,strerror(errno));
108
109         if(rc == ENOTTY)
110                 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);
111         else if (rc > 0)
112                 printf("user unmount of %s failed with %d %s\n",dir,errno,strerror(errno));
113         close(fileid);
114
115         return rc;
116 }
117
118 int main(int argc, char ** argv)
119 {
120         int c;
121         int rc;
122         int flags = 0;
123         int nomtab = 0;
124         int retry_remount = 0;
125         struct mntent mountent;
126         struct statfs statbuf;
127         char * mountpoint;
128         FILE * pmntfile;
129
130         if(argc && argv) {
131                 thisprogram = argv[0];
132         } else {
133                 umount_cifs_usage();
134                 return -EINVAL;
135         }
136
137         if(argc < 2) {
138                 umount_cifs_usage();
139                 return -EINVAL;
140         }
141
142         if(thisprogram == NULL)
143                 thisprogram = "umount.cifs";
144
145         /* add sharename in opts string as unc= parm */
146
147         while ((c = getopt_long (argc, argv, "afhilnrvV",
148                          longopts, NULL)) != -1) {
149                 switch (c) {
150 /* No code to do the following  option yet */
151 /*              case 'a':              
152                         ++umount_all;
153                         break; */
154                 case '?':
155                 case 'h':   /* help */
156                         umount_cifs_usage();
157                         exit(1);
158                 case 'n':
159                         ++nomtab;
160                         break;
161                 case 'f':
162                         flags |= MNT_FORCE;
163                         break;
164                 case 'l':
165                         flags |= MNT_DETACH; /* lazy unmount */
166                         break;
167                 case 'e':
168                         flags |= MNT_EXPIRE; /* gradually timeout */
169                         break;
170                 case 'r':
171                         ++retry_remount;
172                         break;
173                 case 'v':
174                         ++verboseflg;
175                         break;
176                 case 'V':          
177                         printf ("umount.cifs version: %s.%s%s\n",
178                                 UNMOUNT_CIFS_VERSION_MAJOR,
179                                 UNMOUNT_CIFS_VERSION_MINOR,
180                                 UNMOUNT_CIFS_VENDOR_SUFFIX);
181                         exit (0);
182                 default:
183                         printf("unknown unmount option %c\n",c);
184                         umount_cifs_usage();
185                         exit(1);
186                 }
187         }
188
189         /* move past the umount options */
190         argv += optind;
191         argc -= optind;
192
193         mountpoint = argv[0];
194
195         if((argc < 1) || (argv[0] == NULL)) {
196                 printf("\nMissing name of unmount directory\n");
197                 umount_cifs_usage();
198                 return -EINVAL;
199         }
200
201         if(verboseflg)
202                 printf("optind %d unmount dir %s\n",optind, mountpoint);
203
204         /* check if running effectively root */
205         if(geteuid() != 0) {
206                 printf("Trying to unmount when %s not installed suid\n",thisprogram);
207                 if(verboseflg)
208                         printf("euid = %d\n",geteuid());
209                 return -EACCES;
210         }
211
212         /* fixup path if needed */
213
214         /* make sure that this is a cifs filesystem */
215         rc = statfs(mountpoint, &statbuf);
216         
217         if(rc || (statbuf.f_type != CIFS_MAGIC_NUMBER)) {
218                 printf("Wrong filesystem. This utility only unmounts cifs filesystems.\n");
219                 return -EINVAL;
220         }
221
222         /* check if our uid was the one who mounted */
223         rc = umount_check_perm(mountpoint);
224         if (rc) {
225                 printf("Not permitted to unmount\n");
226                 return rc;
227         }
228
229         if(umount2(mountpoint, flags)) {
230         /* remember to kill daemon on error */
231                 switch (errno) {
232                 case 0:
233                         printf("unmount failed but no error number set\n");
234                         break;
235                 default:
236                         printf("unmount error %d = %s\n",errno,strerror(errno));
237                 }
238                 printf("Refer to the umount.cifs(8) manual page (e.g.man 8 umount.cifs)\n");
239                 return -1;
240         } else {
241                 pmntfile = setmntent(MOUNTED, "a+");
242                 if(pmntfile) {
243 /*                      mountent.mnt_fsname = share_name;
244                         mountent.mnt_dir = mountpoint; 
245                         mountent.mnt_type = "cifs"; 
246                         mountent.mnt_opts = malloc(220);
247                         if(mountent.mnt_opts) {
248                                 char * mount_user = getusername();
249                                 memset(mountent.mnt_opts,0,200);
250                                 if(flags & MS_RDONLY)
251                                         strcat(mountent.mnt_opts,"ro");
252                                 else
253                                         strcat(mountent.mnt_opts,"rw");
254                                 if(flags & MS_MANDLOCK)
255                                         strcat(mountent.mnt_opts,",mand");
256                                 else
257                                         strcat(mountent.mnt_opts,",nomand");
258                                 if(flags & MS_NOEXEC)
259                                         strcat(mountent.mnt_opts,",noexec");
260                                 if(flags & MS_NOSUID)
261                                         strcat(mountent.mnt_opts,",nosuid");
262                                 if(flags & MS_NODEV)
263                                         strcat(mountent.mnt_opts,",nodev");
264                                 if(flags & MS_SYNCHRONOUS)
265                                         strcat(mountent.mnt_opts,",synch");
266                                 if(mount_user) {
267                                         if(getuid() != 0) {
268                                                 strcat(mountent.mnt_opts,",user=");
269                                                 strcat(mountent.mnt_opts,mount_user);
270                                         }
271                                         free(mount_user);
272                                 }
273                         }
274                         mountent.mnt_freq = 0;
275                         mountent.mnt_passno = 0;
276                         rc = addmntent(pmntfile,&mountent);
277                         endmntent(pmntfile);
278                         if(mountent.mnt_opts)
279                                 free(mountent.mnt_opts);*/
280                 } else {
281                     printf("could not update mount table\n");
282                 }
283         }
284
285         return 0;
286 }
287