~ubuntu-branches/ubuntu/natty/autofs5/natty-proposed

« back to all changes in this revision

Viewing changes to lib/parse_subs.c

  • Committer: Bazaar Package Importer
  • Author(s): Jan Christoph Nordholz
  • Date: 2008-04-28 15:55:37 UTC
  • Revision ID: james.westby@ubuntu.com-20080428155537-h6h457h1fwwzhvby
Tags: upstream-5.0.3
ImportĀ upstreamĀ versionĀ 5.0.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* ----------------------------------------------------------------------- *
 
2
 *   
 
3
 *  parse_subs.c - misc parser subroutines
 
4
 *                automounter map
 
5
 * 
 
6
 *   Copyright 1997 Transmeta Corporation - All Rights Reserved
 
7
 *   Copyright 2000 Jeremy Fitzhardinge <jeremy@goop.org>
 
8
 *   Copyright 2004-2006 Ian Kent <raven@themaw.net>
 
9
 *
 
10
 *   This program is free software; you can redistribute it and/or modify
 
11
 *   it under the terms of the GNU General Public License as published by
 
12
 *   the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139,
 
13
 *   USA; either version 2 of the License, or (at your option) any later
 
14
 *   version; incorporated herein by reference.
 
15
 *
 
16
 * ----------------------------------------------------------------------- */
 
17
 
 
18
#include <stdlib.h>
 
19
#include <string.h>
 
20
#include <ctype.h>
 
21
#include <sys/types.h>
 
22
#include <sys/stat.h>
 
23
#include <unistd.h>
 
24
#include <sys/vfs.h>
 
25
#include "automount.h"
 
26
 
 
27
/*
 
28
 * Skip whitespace in a string; if we hit a #, consider the rest of the
 
29
 * entry a comment.
 
30
 */
 
31
const char *skipspace(const char *whence)
 
32
{
 
33
        while (1) {
 
34
                switch (*whence) {
 
35
                case ' ':
 
36
                case '\b':
 
37
                case '\t':
 
38
                case '\n':
 
39
                case '\v':
 
40
                case '\f':
 
41
                case '\r':
 
42
                        whence++;
 
43
                        break;
 
44
                case '#':       /* comment: skip to end of string */
 
45
                        while (*whence != '\0')
 
46
                                whence++;
 
47
                        /* FALLTHROUGH */
 
48
 
 
49
                default:
 
50
                        return whence;
 
51
                }
 
52
        }
 
53
}
 
54
 
 
55
/*
 
56
 * Check a string to see if a colon appears before the next '/'.
 
57
 */
 
58
int check_colon(const char *str)
 
59
{
 
60
        char *ptr = (char *) str;
 
61
 
 
62
        /* Colon escape */
 
63
        if (*ptr == ':')
 
64
                return 1;
 
65
 
 
66
        while (*ptr && *ptr != ':' && *ptr != '/') {
 
67
                ptr++;
 
68
        }
 
69
 
 
70
        if (!*ptr || *ptr == '/')
 
71
                return 0;
 
72
 
 
73
        return 1;
 
74
}
 
75
 
 
76
/* Get the length of a chunk delimitered by whitespace */
 
77
int chunklen(const char *whence, int expect_colon)
 
78
{
 
79
        char *str = (char *) whence;
 
80
        int n = 0;
 
81
        int quote = 0;
 
82
 
 
83
        for (; *str; str++, n++) {
 
84
                switch (*str) {
 
85
                case '\\':
 
86
                        if( quote ) {
 
87
                                break;
 
88
                        } else {
 
89
                                quote = 1;
 
90
                                continue;
 
91
                        }
 
92
                case '"':
 
93
                        if (quote)
 
94
                                break;
 
95
                        while (*str) {
 
96
                                str++;
 
97
                                n++;
 
98
                                if (*str == '"')
 
99
                                        break;
 
100
                                if (*str == ':')
 
101
                                        expect_colon = 0;
 
102
                        }
 
103
                        break;
 
104
                case ':':
 
105
                        if (expect_colon)
 
106
                                expect_colon = 0;
 
107
                        continue;
 
108
                case ' ':
 
109
                case '\t':
 
110
                        /* Skip space or tab if we expect a colon */
 
111
                        if (expect_colon)
 
112
                                continue;
 
113
                case '\b':
 
114
                case '\n':
 
115
                case '\v':
 
116
                case '\f':
 
117
                case '\r':
 
118
                case '\0':
 
119
                        if (!quote)
 
120
                                return n;
 
121
                        /* FALLTHROUGH */
 
122
                default:
 
123
                        break;
 
124
                }
 
125
                quote = 0;
 
126
        }
 
127
 
 
128
        return n;
 
129
}
 
130
 
 
131
/*
 
132
 * Compare str with pat.  Return 0 if compare equal or
 
133
 * str is an abbreviation of pat of no less than mchr characters.
 
134
 */
 
135
int strmcmp(const char *str, const char *pat, int mchr)
 
136
{
 
137
        int nchr = 0;
 
138
 
 
139
        while (*str == *pat) {
 
140
                if (!*str)
 
141
                        return 0;
 
142
                str++;
 
143
                pat++;
 
144
                nchr++;
 
145
        }
 
146
 
 
147
        if (!*str && nchr > mchr)
 
148
                return 0;
 
149
 
 
150
        return *pat - *str;
 
151
}
 
152
 
 
153
char *dequote(const char *str, int origlen, unsigned int logopt)
 
154
{
 
155
        char *ret = malloc(origlen + 1);
 
156
        char *cp = ret;
 
157
        const char *scp;
 
158
        int len = origlen;
 
159
        int quote = 0, dquote = 0;
 
160
        int i, j;
 
161
 
 
162
        if (ret == NULL)
 
163
                return NULL;
 
164
 
 
165
        /* first thing to do is strip white space from the end */
 
166
        i = len - 1;
 
167
        while (isspace(str[i])) {
 
168
                /* of course, we have to keep escaped white-space */
 
169
                j = i - 1;
 
170
                if (j > 0 && (str[j] == '\\' || str[j] == '"'))
 
171
                        break;
 
172
                i--;
 
173
                len--;
 
174
        }
 
175
 
 
176
        for (scp = str; len > 0 && *scp; scp++, len--) {
 
177
                if (!quote) {
 
178
                        if (*scp == '"') {
 
179
                                if (dquote)
 
180
                                        dquote = 0;
 
181
                                else
 
182
                                        dquote = 1;
 
183
                                continue;
 
184
                        }
 
185
 
 
186
                        if (!dquote) {
 
187
                                if (*scp == '\\') {
 
188
                                        quote = 1;
 
189
                                        continue;
 
190
                                }
 
191
                        }
 
192
                }
 
193
                quote = 0;
 
194
                *cp++ = *scp;
 
195
        }
 
196
        *cp = '\0';
 
197
 
 
198
        if (dquote) {
 
199
                debug(logopt, "unmatched quote in %.*s", origlen, str);
 
200
                free(ret);
 
201
                return NULL;
 
202
        }
 
203
 
 
204
        return ret;
 
205
}
 
206
 
 
207
int span_space(const char *str, unsigned int maxlen)
 
208
{
 
209
        const char *p = str;
 
210
        unsigned int len = 0;
 
211
 
 
212
        while (*p && !isblank(*p) && len < maxlen) {
 
213
                if (*p == '"') {
 
214
                        while (*p++ && len++ < maxlen) {
 
215
                                if (*p == '"')
 
216
                                        break;
 
217
                        }
 
218
                } else if (*p == '\\') {
 
219
                        p += 2;
 
220
                        len += 2;
 
221
                        continue;
 
222
                }
 
223
                p++;
 
224
                len++;
 
225
        }
 
226
        return len;
 
227
}
 
228
 
 
229
char *sanitize_path(const char *path, int origlen, unsigned int type, unsigned int logopt)
 
230
{
 
231
        char *slash, *cp, *s_path;
 
232
        const char *scp;
 
233
        int len = origlen;
 
234
        unsigned int seen_slash = 0, quote = 0, dquote = 0;
 
235
 
 
236
        if (type & (LKP_INDIRECT | LKP_DIRECT)) {
 
237
                slash = strchr(path, '/');
 
238
                if (slash) {
 
239
                        if (type == LKP_INDIRECT)
 
240
                                return NULL;
 
241
                        if (*path != '/')
 
242
                                return NULL;
 
243
                } else {
 
244
                        if (type == LKP_DIRECT)
 
245
                                return NULL;
 
246
                }
 
247
        }
 
248
 
 
249
        s_path = malloc(origlen + 1);
 
250
        if (!s_path)
 
251
                return NULL;
 
252
 
 
253
        for (cp = s_path, scp = path; len > 0; scp++, len--) {
 
254
                if (!quote) {
 
255
                        if (*scp == '"') {
 
256
                                if (dquote)
 
257
                                        dquote = 0;
 
258
                                else
 
259
                                        dquote = 1;
 
260
                                continue;
 
261
                        }
 
262
 
 
263
                        if (!dquote) {
 
264
                                /* Badness in string - go away */
 
265
                                if (*scp < 32) {
 
266
                                        free(s_path);
 
267
                                        return NULL;
 
268
                                }
 
269
 
 
270
                                if (*scp == '\\') {
 
271
                                        quote = 1;
 
272
                                        continue;
 
273
                                } 
 
274
                        }
 
275
 
 
276
                        /*
 
277
                         * Not really proper but we get problems with
 
278
                         * paths with multiple slashes. The kernel
 
279
                         * compresses them so when we get a query there
 
280
                         * should be only single slashes.
 
281
                         */
 
282
                        if (*scp == '/') {
 
283
                                if (seen_slash)
 
284
                                        continue;
 
285
                                seen_slash = 1;
 
286
                        } else
 
287
                                seen_slash = 0;
 
288
                }
 
289
                quote = 0;
 
290
                *cp++ = *scp;
 
291
        }
 
292
        *cp = '\0';
 
293
 
 
294
        if (dquote) {
 
295
                debug(logopt, "unmatched quote in %.*s", origlen, path);
 
296
                free(s_path);
 
297
                return NULL;
 
298
        }
 
299
 
 
300
        /* Remove trailing / but watch out for a quoted / alone */
 
301
        if (strlen(cp) > 1 && origlen > 1 && *(cp - 1) == '/')
 
302
                *(cp - 1) = '\0';
 
303
 
 
304
        return s_path;
 
305
}
 
306
 
 
307
int umount_ent(struct autofs_point *ap, const char *path)
 
308
{
 
309
        struct stat st;
 
310
        struct statfs fs;
 
311
        int sav_errno;
 
312
        int status, is_smbfs = 0;
 
313
        int ret, rv = 1;
 
314
 
 
315
        ret = statfs(path, &fs);
 
316
        if (ret == -1) {
 
317
                warn(ap->logopt, "could not stat fs of %s", path);
 
318
                is_smbfs = 0;
 
319
        } else {
 
320
                int cifsfs = fs.f_type == (__SWORD_TYPE) CIFS_MAGIC_NUMBER;
 
321
                int smbfs = fs.f_type == (__SWORD_TYPE) SMB_SUPER_MAGIC;
 
322
                is_smbfs = (cifsfs | smbfs) ? 1 : 0;
 
323
        }
 
324
 
 
325
        status = lstat(path, &st);
 
326
        sav_errno = errno;
 
327
 
 
328
        if (status < 0)
 
329
                warn(ap->logopt, "lstat of %s failed with %d", path, status);
 
330
 
 
331
        /*
 
332
         * lstat failed and we're an smbfs fs returning an error that is not
 
333
         * EIO or EBADSLT or the lstat failed so it's a bad path. Return
 
334
         * a fail.
 
335
         *
 
336
         * EIO appears to correspond to an smb mount that has gone away
 
337
         * and EBADSLT relates to CD changer not responding.
 
338
         */
 
339
        if (!status && (S_ISDIR(st.st_mode) && st.st_dev != ap->dev)) {
 
340
                rv = spawn_umount(ap->logopt, path, NULL);
 
341
        } else if (is_smbfs && (sav_errno == EIO || sav_errno == EBADSLT)) {
 
342
                rv = spawn_umount(ap->logopt, path, NULL);
 
343
        }
 
344
 
 
345
        /* We are doing a forced shutcwdown down so unlink busy mounts */
 
346
        if (rv && (ap->state == ST_SHUTDOWN_FORCE || ap->state == ST_SHUTDOWN)) {
 
347
                ret = stat(path, &st);
 
348
                if (ret == -1 && errno == ENOENT) {
 
349
                        warn(ap->logopt, "mount point does not exist");
 
350
                        return 0;
 
351
                }
 
352
 
 
353
                if (ret == 0 && !S_ISDIR(st.st_mode)) {
 
354
                        warn(ap->logopt, "mount point is not a directory");
 
355
                        return 0;
 
356
                }
 
357
 
 
358
                if (ap->state == ST_SHUTDOWN_FORCE) {
 
359
                        info(ap->logopt, "forcing umount of %s", path);
 
360
                        rv = spawn_umount(ap->logopt, "-l", path, NULL);
 
361
                }
 
362
 
 
363
                /*
 
364
                 * Verify that we actually unmounted the thing.  This is a
 
365
                 * belt and suspenders approach to not eating user data.
 
366
                 * We have seen cases where umount succeeds, but there is
 
367
                 * still a file system mounted on the mount point.  How
 
368
                 * this happens has not yet been determined, but we want to
 
369
                 * make sure to return failure here, if that is the case,
 
370
                 * so that we do not try to call rmdir_path on the
 
371
                 * directory.
 
372
                 */
 
373
                if (!rv && is_mounted(_PATH_MOUNTED, path, MNTS_REAL)) {
 
374
                        crit(ap->logopt,
 
375
                             "the umount binary reported that %s was "
 
376
                             "unmounted, but there is still something "
 
377
                             "mounted on this path.", path);
 
378
                        rv = -1;
 
379
                }
 
380
        }
 
381
 
 
382
        return rv;
 
383
}
 
384
 
 
385
int mount_multi_triggers(struct autofs_point *ap, char *root, struct mapent *me, const char *base)
 
386
{
 
387
        char path[PATH_MAX + 1];
 
388
        char *offset = path;
 
389
        struct mapent *oe;
 
390
        struct list_head *pos = NULL;
 
391
        unsigned int fs_path_len;
 
392
        unsigned int mounted;
 
393
        int start;
 
394
 
 
395
        fs_path_len = strlen(root) + strlen(base);
 
396
        if (fs_path_len > PATH_MAX)
 
397
                return -1;
 
398
 
 
399
        strcpy(path, root);
 
400
        strcat(path, base);
 
401
 
 
402
        mounted = 0;
 
403
        start = strlen(root);
 
404
        offset = cache_get_offset(base, offset, start, &me->multi_list, &pos);
 
405
        while (offset) {
 
406
                int plen = fs_path_len + strlen(offset);
 
407
 
 
408
                if (plen > PATH_MAX) {
 
409
                        warn(ap->logopt, "path loo long");
 
410
                        goto cont;
 
411
                }
 
412
 
 
413
                oe = cache_lookup_offset(base, offset, start, &me->multi_list);
 
414
                if (!oe)
 
415
                        goto cont;
 
416
 
 
417
                debug(ap->logopt, "mount offset %s", oe->key);
 
418
 
 
419
                if (mount_autofs_offset(ap, oe) < 0)
 
420
                        warn(ap->logopt, "failed to mount offset");
 
421
                else
 
422
                        mounted++;
 
423
cont:
 
424
                offset = cache_get_offset(base,
 
425
                                offset, start, &me->multi_list, &pos);
 
426
        }
 
427
 
 
428
        return mounted;
 
429
}
 
430
 
 
431
int umount_multi_triggers(struct autofs_point *ap, char *root, struct mapent *me, const char *base)
 
432
{
 
433
        char path[PATH_MAX + 1];
 
434
        char *offset;
 
435
        struct mapent *oe;
 
436
        struct list_head *mm_root, *pos;
 
437
        const char o_root[] = "/";
 
438
        const char *mm_base;
 
439
        int left, start;
 
440
 
 
441
        left = 0;
 
442
        start = strlen(root);
 
443
 
 
444
        mm_root = &me->multi->multi_list;
 
445
 
 
446
        if (!base)
 
447
                mm_base = o_root;
 
448
        else
 
449
                mm_base = base;
 
450
 
 
451
        pos = NULL;
 
452
        offset = path;
 
453
 
 
454
        /* Make sure "none" of the offsets have an active mount. */
 
455
        while ((offset = cache_get_offset(mm_base, offset, start, mm_root, &pos))) {
 
456
                char *oe_base;
 
457
 
 
458
                oe = cache_lookup_offset(mm_base, offset, start, &me->multi_list);
 
459
                /* root offset is a special case */
 
460
                if (!oe || (strlen(oe->key) - start) == 1)
 
461
                        continue;
 
462
 
 
463
                /*
 
464
                 * Check for and umount subtree offsets resulting from
 
465
                 * nonstrict mount fail.
 
466
                 */
 
467
                oe_base = oe->key + strlen(root);
 
468
                left += umount_multi_triggers(ap, root, oe, oe_base);
 
469
 
 
470
                if (oe->ioctlfd != -1)
 
471
                        left++;
 
472
        }
 
473
 
 
474
        if (left)
 
475
                return left;
 
476
 
 
477
        pos = NULL;
 
478
        offset = path;
 
479
 
 
480
        /* Make sure "none" of the offsets have an active mount. */
 
481
        while ((offset = cache_get_offset(mm_base, offset, start, mm_root, &pos))) {
 
482
                oe = cache_lookup_offset(mm_base, offset, start, &me->multi_list);
 
483
                /* root offset is a special case */
 
484
                if (!oe || (strlen(oe->key) - start) == 1)
 
485
                        continue;
 
486
 
 
487
                debug(ap->logopt, "umount offset %s", oe->key);
 
488
 
 
489
                if (umount_autofs_offset(ap, oe)) {
 
490
                        warn(ap->logopt, "failed to umount offset");
 
491
                        left++;
 
492
                }
 
493
        }
 
494
 
 
495
        if (!left && me->multi == me) {
 
496
                struct mapent_cache *mc = me->mc;
 
497
                int status;
 
498
 
 
499
                /*
 
500
                 * Special case.
 
501
                 * If we can't umount the root container then we can't
 
502
                 * delete the offsets from the cache and we need to put
 
503
                 * the offset triggers back.
 
504
                 */
 
505
                if (is_mounted(_PATH_MOUNTED, root, MNTS_REAL)) {
 
506
                        info(ap->logopt, "unmounting dir = %s", root);
 
507
                        if (umount_ent(ap, root)) {
 
508
                                if (!mount_multi_triggers(ap, root, me, "/"))
 
509
                                        warn(ap->logopt,
 
510
                                             "failed to remount offset triggers");
 
511
                                return left++;
 
512
                        }
 
513
                }
 
514
 
 
515
                /* We're done - clean out the offsets */
 
516
                status = cache_delete_offset_list(mc, me->key);
 
517
                if (status != CHE_OK)
 
518
                        warn(ap->logopt, "couldn't delete offset list");
 
519
        }
 
520
 
 
521
        return left;
 
522
}
 
523