~zulcss/samba/server-dailies-3.4

« back to all changes in this revision

Viewing changes to source3/client/umount.cifs.c

  • Committer: Chuck Short
  • Date: 2010-09-28 20:38:39 UTC
  • Revision ID: zulcss@ubuntu.com-20100928203839-pgjulytsi9ue63x1
Initial version

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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 3 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, see <http://www.gnu.org/licenses/>.  */
 
17
 
 
18
#ifndef _GNU_SOURCE
 
19
#define _GNU_SOURCE
 
20
#endif
 
21
 
 
22
#include <stdlib.h>
 
23
#include <stdio.h>
 
24
#include <unistd.h>
 
25
#include <ctype.h>
 
26
#include <sys/types.h>
 
27
#include <sys/mount.h>
 
28
#include <sys/ioctl.h>
 
29
#include <sys/stat.h>
 
30
#include <sys/vfs.h>
 
31
#include <fcntl.h>
 
32
#include <getopt.h>
 
33
#include <errno.h>
 
34
#include <string.h>
 
35
#include <mntent.h>
 
36
#include <limits.h>
 
37
#include "mount.h"
 
38
 
 
39
#define UNMOUNT_CIFS_VERSION_MAJOR "0"
 
40
#define UNMOUNT_CIFS_VERSION_MINOR "6"
 
41
 
 
42
#ifndef UNMOUNT_CIFS_VENDOR_SUFFIX
 
43
 #ifdef _SAMBA_BUILD_
 
44
  #include "include/version.h"
 
45
  #ifdef SAMBA_VERSION_VENDOR_SUFFIX
 
46
   #define UNMOUNT_CIFS_VENDOR_SUFFIX "-"SAMBA_VERSION_OFFICIAL_STRING"-"SAMBA_VERSION_VENDOR_SUFFIX
 
47
  #else
 
48
   #define UNMOUNT_CIFS_VENDOR_SUFFIX "-"SAMBA_VERSION_OFFICIAL_STRING
 
49
  #endif /* SAMBA_VERSION_OFFICIAL_STRING and SAMBA_VERSION_VENDOR_SUFFIX */
 
50
 #else
 
51
  #define UNMOUNT_CIFS_VENDOR_SUFFIX ""
 
52
 #endif /* _SAMBA_BUILD_ */
 
53
#endif /* UNMOUNT_CIFS_VENDOR_SUFFIX */
 
54
 
 
55
#ifndef MNT_DETACH
 
56
#define MNT_DETACH 0x02
 
57
#endif
 
58
 
 
59
#ifndef MNT_EXPIRE
 
60
#define MNT_EXPIRE 0x04
 
61
#endif
 
62
 
 
63
#ifndef MOUNTED_LOCK
 
64
#define MOUNTED_LOCK    "/etc/mtab~"
 
65
#endif
 
66
#ifndef MOUNTED_TEMP
 
67
#define MOUNTED_TEMP    "/etc/mtab.tmp"
 
68
#endif
 
69
 
 
70
#define CIFS_IOC_CHECKUMOUNT _IO(0xCF, 2)
 
71
#define CIFS_MAGIC_NUMBER 0xFF534D42   /* the first four bytes of SMB PDU */
 
72
   
 
73
static struct option longopts[] = {
 
74
        { "all", 0, NULL, 'a' },
 
75
        { "help",0, NULL, 'h' },
 
76
        { "read-only", 0, NULL, 'r' },
 
77
        { "ro", 0, NULL, 'r' },
 
78
        { "verbose", 0, NULL, 'v' },
 
79
        { "version", 0, NULL, 'V' },
 
80
        { "expire", 0, NULL, 'e' },
 
81
        { "force", 0, 0, 'f' },
 
82
        { "lazy", 0, 0, 'l' },
 
83
        { "no-mtab", 0, 0, 'n' },
 
84
        { NULL, 0, NULL, 0 }
 
85
};
 
86
 
 
87
const char * thisprogram;
 
88
int verboseflg = 0;
 
89
 
 
90
static void umount_cifs_usage(void)
 
91
{
 
92
        printf("\nUsage:  %s <remotetarget> <dir>\n", thisprogram);
 
93
        printf("\nUnmount the specified directory\n");
 
94
        printf("\nLess commonly used options:");
 
95
        printf("\n\t-r\tIf mount fails, retry with readonly remount.");
 
96
        printf("\n\t-n\tDo not write to mtab.");
 
97
        printf("\n\t-f\tAttempt a forced unmount, even if the fs is busy.");
 
98
        printf("\n\t-l\tAttempt lazy unmount, Unmount now, cleanup later.");
 
99
        printf("\n\t-v\tEnable verbose mode (may be useful for debugging).");
 
100
        printf("\n\t-h\tDisplay this help.");
 
101
        printf("\n\nOptions are described in more detail in the manual page");
 
102
        printf("\n\tman 8 umount.cifs\n");
 
103
        printf("\nTo display the version number of the cifs umount utility:");
 
104
        printf("\n\t%s -V\n",thisprogram);
 
105
        printf("\nInvoking the umount utility on cifs mounts, can execute");
 
106
        printf(" /sbin/umount.cifs (if present and umount -i is not specified.\n");
 
107
}
 
108
 
 
109
static int umount_check_perm(char * dir)
 
110
{
 
111
        int fileid;
 
112
        int rc;
 
113
 
 
114
        /* allow root to unmount, no matter what */
 
115
        if(getuid() == 0)
 
116
                return 0;
 
117
 
 
118
        /* presumably can not chdir into the target as we do on mount */
 
119
        fileid = open(dir, O_RDONLY | O_DIRECTORY | O_NOFOLLOW, 0);
 
120
        if(fileid == -1) {
 
121
                if(verboseflg)
 
122
                        printf("error opening mountpoint %d %s",errno,strerror(errno));
 
123
                return errno;
 
124
        }
 
125
 
 
126
        rc = ioctl(fileid, CIFS_IOC_CHECKUMOUNT, NULL);
 
127
 
 
128
        if(verboseflg)
 
129
                printf("ioctl returned %d with errno %d %s\n",rc,errno,strerror(errno));
 
130
 
 
131
        if(rc == ENOTTY) {
 
132
                printf("user unmounting via %s is an optional feature of",thisprogram);
 
133
                printf(" the cifs filesystem driver (cifs.ko)");
 
134
                printf("\n\tand requires cifs.ko version 1.32 or later\n");
 
135
        } else if (rc != 0)
 
136
                printf("user unmount of %s failed with %d %s\n",dir,errno,strerror(errno));
 
137
        close(fileid);
 
138
 
 
139
        return rc;
 
140
}
 
141
 
 
142
static int remove_from_mtab(char * mountpoint)
 
143
{
 
144
        int rc;
 
145
        int num_matches;
 
146
        FILE * org_fd;
 
147
        FILE * new_fd;
 
148
        struct mntent * mount_entry;
 
149
        struct stat statbuf;
 
150
 
 
151
        /* If it is a symlink, e.g. to /proc/mounts, no need to update it. */
 
152
        if ((lstat(MOUNTED, &statbuf) == 0) && (S_ISLNK(statbuf.st_mode)))
 
153
                return 0;
 
154
 
 
155
        /* Do we first need to check if it is writable? */ 
 
156
 
 
157
        atexit(unlock_mtab);
 
158
        if (lock_mtab()) {
 
159
                printf("Mount table locked\n");
 
160
                return -EACCES;
 
161
        }
 
162
        
 
163
        if(verboseflg)
 
164
                printf("attempting to remove from mtab\n");
 
165
 
 
166
        org_fd = setmntent(MOUNTED, "r");
 
167
        if(org_fd == NULL) {
 
168
                printf("Can not open %s\n",MOUNTED);
 
169
                unlock_mtab();
 
170
                return -EIO;
 
171
        }
 
172
 
 
173
        new_fd = setmntent(MOUNTED_TEMP,"w");
 
174
        if(new_fd == NULL) {
 
175
                printf("Can not open temp file %s", MOUNTED_TEMP);
 
176
                endmntent(org_fd);
 
177
                unlock_mtab();
 
178
                return -EIO;
 
179
        }
 
180
 
 
181
        /* BB fix so we only remove the last entry that matches BB */
 
182
        num_matches = 0;
 
183
        while((mount_entry = getmntent(org_fd)) != NULL) {
 
184
                if(strcmp(mount_entry->mnt_dir, mountpoint) == 0) {
 
185
                        num_matches++;
 
186
                }
 
187
        }       
 
188
        if(verboseflg)
 
189
                printf("%d matching entries in mount table\n", num_matches);
 
190
                
 
191
        /* Is there a better way to seek back to the first entry in mtab? */
 
192
        endmntent(org_fd);
 
193
        org_fd = setmntent(MOUNTED, "r");
 
194
 
 
195
        if(org_fd == NULL) {
 
196
                printf("Can not open %s\n",MOUNTED);
 
197
                unlock_mtab();
 
198
                return -EIO;
 
199
        }
 
200
        
 
201
        while((mount_entry = getmntent(org_fd)) != NULL) {
 
202
                if(strcmp(mount_entry->mnt_dir, mountpoint) != 0) {
 
203
                        addmntent(new_fd, mount_entry);
 
204
                } else {
 
205
                        if(num_matches != 1) {
 
206
                                addmntent(new_fd, mount_entry);
 
207
                                num_matches--;
 
208
                        } else if(verboseflg)
 
209
                                printf("entry not copied (ie entry is removed)\n");
 
210
                }
 
211
        }
 
212
 
 
213
        if(verboseflg)
 
214
                printf("done updating tmp file\n");
 
215
        rc = fchmod (fileno (new_fd), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
 
216
        if(rc < 0) {
 
217
                printf("error %s changing mode of %s\n", strerror(errno),
 
218
                        MOUNTED_TEMP);
 
219
        }
 
220
        endmntent(new_fd);
 
221
 
 
222
        rc = rename(MOUNTED_TEMP, MOUNTED);
 
223
 
 
224
        if(rc < 0) {
 
225
                printf("failure %s renaming %s to %s\n",strerror(errno),
 
226
                        MOUNTED_TEMP, MOUNTED);
 
227
                unlock_mtab();
 
228
                return -EIO;
 
229
        }
 
230
 
 
231
        unlock_mtab();
 
232
        
 
233
        return rc;
 
234
}
 
235
 
 
236
/* Make a canonical pathname from PATH.  Returns a freshly malloced string.
 
237
   It is up the *caller* to ensure that the PATH is sensible.  i.e.
 
238
   canonicalize ("/dev/fd0/.") returns "/dev/fd0" even though ``/dev/fd0/.''
 
239
   is not a legal pathname for ``/dev/fd0''  Anything we cannot parse
 
240
   we return unmodified.   */
 
241
static char *
 
242
canonicalize(char *path)
 
243
{
 
244
        char *canonical;
 
245
 
 
246
        if (path == NULL) {
 
247
                return NULL;
 
248
        }
 
249
 
 
250
        if (strlen(path) > PATH_MAX) {
 
251
                fprintf(stderr, "Mount point string too long\n");
 
252
                return NULL;
 
253
        }
 
254
 
 
255
        canonical = (char *)malloc (PATH_MAX + 1);
 
256
 
 
257
        if (!canonical) {
 
258
                fprintf(stderr, "Error! Not enough memory!\n");
 
259
                return NULL;
 
260
        }
 
261
 
 
262
        if (realpath (path, canonical))
 
263
                return canonical;
 
264
 
 
265
        strncpy (canonical, path, PATH_MAX);
 
266
        canonical[PATH_MAX] = '\0';
 
267
        return canonical;
 
268
}
 
269
 
 
270
int main(int argc, char ** argv)
 
271
{
 
272
        int c;
 
273
        int rc;
 
274
        int flags = 0;
 
275
        int nomtab = 0;
 
276
        int retry_remount = 0;
 
277
        struct statfs statbuf;
 
278
        char * mountpoint;
 
279
 
 
280
        if(argc && argv) {
 
281
                thisprogram = argv[0];
 
282
        } else {
 
283
                umount_cifs_usage();
 
284
                return -EINVAL;
 
285
        }
 
286
 
 
287
        if(argc < 2) {
 
288
                umount_cifs_usage();
 
289
                return -EINVAL;
 
290
        }
 
291
 
 
292
        if(thisprogram == NULL)
 
293
                thisprogram = "umount.cifs";
 
294
 
 
295
        /* add sharename in opts string as unc= parm */
 
296
 
 
297
        while ((c = getopt_long (argc, argv, "afhilnrvV",
 
298
                         longopts, NULL)) != -1) {
 
299
                switch (c) {
 
300
/* No code to do the following  option yet */
 
301
/*              case 'a':              
 
302
                        ++umount_all;
 
303
                        break; */
 
304
                case '?':
 
305
                case 'h':   /* help */
 
306
                        umount_cifs_usage();
 
307
                        exit(1);
 
308
                case 'n':
 
309
                        ++nomtab;
 
310
                        break;
 
311
                case 'f':
 
312
                        flags |= MNT_FORCE;
 
313
                        break;
 
314
                case 'l':
 
315
                        flags |= MNT_DETACH; /* lazy unmount */
 
316
                        break;
 
317
                case 'e':
 
318
                        flags |= MNT_EXPIRE; /* gradually timeout */
 
319
                        break;
 
320
                case 'r':
 
321
                        ++retry_remount;
 
322
                        break;
 
323
                case 'v':
 
324
                        ++verboseflg;
 
325
                        break;
 
326
                case 'V':          
 
327
                        printf ("umount.cifs version: %s.%s%s\n",
 
328
                                UNMOUNT_CIFS_VERSION_MAJOR,
 
329
                                UNMOUNT_CIFS_VERSION_MINOR,
 
330
                                UNMOUNT_CIFS_VENDOR_SUFFIX);
 
331
                        exit (0);
 
332
                default:
 
333
                        printf("unknown unmount option %c\n",c);
 
334
                        umount_cifs_usage();
 
335
                        exit(1);
 
336
                }
 
337
        }
 
338
 
 
339
        /* move past the umount options */
 
340
        argv += optind;
 
341
        argc -= optind;
 
342
 
 
343
        mountpoint = canonicalize(argv[0]);
 
344
 
 
345
        if((argc < 1) || (argv[0] == NULL)) {
 
346
                printf("\nMissing name of unmount directory\n");
 
347
                umount_cifs_usage();
 
348
                return -EINVAL;
 
349
        }
 
350
 
 
351
        if(verboseflg)
 
352
                printf("optind %d unmount dir %s\n",optind, mountpoint);
 
353
 
 
354
        /* check if running effectively root */
 
355
        if(geteuid() != 0) {
 
356
                printf("Trying to unmount when %s not installed suid\n",thisprogram);
 
357
                if(verboseflg)
 
358
                        printf("euid = %d\n",geteuid());
 
359
                return -EACCES;
 
360
        }
 
361
 
 
362
        /* fixup path if needed */
 
363
 
 
364
        /* Trim any trailing slashes */
 
365
        while ((strlen(mountpoint) > 1) &&
 
366
                (mountpoint[strlen(mountpoint)-1] == '/'))
 
367
        {
 
368
                mountpoint[strlen(mountpoint)-1] = '\0';
 
369
        }
 
370
 
 
371
        /* make sure that this is a cifs filesystem */
 
372
        rc = statfs(mountpoint, &statbuf);
 
373
        
 
374
        if(rc || (statbuf.f_type != CIFS_MAGIC_NUMBER)) {
 
375
                printf("This utility only unmounts cifs filesystems.\n");
 
376
                return -EINVAL;
 
377
        }
 
378
 
 
379
        /* check if our uid was the one who mounted */
 
380
        rc = umount_check_perm(mountpoint);
 
381
        if (rc) {
 
382
                printf("Not permitted to unmount\n");
 
383
                return rc;
 
384
        }
 
385
 
 
386
        if(umount2(mountpoint, flags)) {
 
387
        /* remember to kill daemon on error */
 
388
                switch (errno) {
 
389
                case 0:
 
390
                        printf("unmount failed but no error number set\n");
 
391
                        break;
 
392
                default:
 
393
                        printf("unmount error %d = %s\n",errno,strerror(errno));
 
394
                }
 
395
                printf("Refer to the umount.cifs(8) manual page (man 8 umount.cifs)\n");
 
396
                return -1;
 
397
        } else {
 
398
                if(verboseflg)
 
399
                        printf("umount2 succeeded\n");
 
400
                if(nomtab == 0)
 
401
                        remove_from_mtab(mountpoint);
 
402
        }
 
403
 
 
404
        return 0;
 
405
}
 
406