Subversion Repositories HelenOS

Rev

Rev 3022 | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 3022 Rev 4055
1
/*
1
/*
2
 * Copyright (c) 2008 Jakub Jermar
2
 * Copyright (c) 2008 Jakub Jermar
3
 * All rights reserved.
3
 * All rights reserved.
4
 *
4
 *
5
 * Redistribution and use in source and binary forms, with or without
5
 * Redistribution and use in source and binary forms, with or without
6
 * modification, are permitted provided that the following conditions
6
 * modification, are permitted provided that the following conditions
7
 * are met:
7
 * are met:
8
 *
8
 *
9
 * - Redistributions of source code must retain the above copyright
9
 * - Redistributions of source code must retain the above copyright
10
 *   notice, this list of conditions and the following disclaimer.
10
 *   notice, this list of conditions and the following disclaimer.
11
 * - Redistributions in binary form must reproduce the above copyright
11
 * - Redistributions in binary form must reproduce the above copyright
12
 *   notice, this list of conditions and the following disclaimer in the
12
 *   notice, this list of conditions and the following disclaimer in the
13
 *   documentation and/or other materials provided with the distribution.
13
 *   documentation and/or other materials provided with the distribution.
14
 * - The name of the author may not be used to endorse or promote products
14
 * - The name of the author may not be used to endorse or promote products
15
 *   derived from this software without specific prior written permission.
15
 *   derived from this software without specific prior written permission.
16
 *
16
 *
17
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
 */
27
 */
28
 
28
 
29
/** @addtogroup libc
29
/** @addtogroup libc
30
 * @{
30
 * @{
31
 */
31
 */
32
 
32
 
33
/**
33
/**
34
 * @file
34
 * @file
35
 * @brief
35
 * @brief
36
 */
36
 */
37
 
37
 
38
#include <stdlib.h>
38
#include <stdlib.h>
-
 
39
#include <vfs/canonify.h>
39
 
40
 
40
/** Token types used for tokenization of path. */
41
/** Token types used for tokenization of path. */
41
typedef enum {
42
typedef enum {
42
    TK_INVALID,
43
    TK_INVALID,
43
    TK_SLASH,
44
    TK_SLASH,
44
    TK_DOT,
45
    TK_DOT,
45
    TK_DOTDOT,
46
    TK_DOTDOT,
46
    TK_COMP,
47
    TK_COMP,
47
    TK_NUL
48
    TK_NUL
48
} tokval_t;
49
} tokval_t;
49
 
50
 
50
typedef struct {
51
typedef struct {
51
    tokval_t kind;
52
    tokval_t kind;
52
    char *start;
53
    char *start;
53
    char *stop;
54
    char *stop;
54
} token_t;
55
} token_t;
55
 
56
 
56
/** Fake up the TK_SLASH token. */
57
/** Fake up the TK_SLASH token. */
57
static token_t slash_token(char *start)
58
static token_t slash_token(char *start)
58
{
59
{
59
    token_t ret;
60
    token_t ret;
60
    ret.kind = TK_SLASH;
61
    ret.kind = TK_SLASH;
61
    ret.start = start;
62
    ret.start = start;
62
    ret.stop = start;
63
    ret.stop = start;
63
    return ret;
64
    return ret;
64
}
65
}
65
 
66
 
66
/** Given a token, return the next token. */
67
/** Given a token, return the next token. */
67
static token_t next_token(token_t *cur)
68
static token_t next_token(token_t *cur)
68
{
69
{
69
    token_t ret;
70
    token_t ret;
70
 
71
 
71
    if (cur->stop[1] == '\0') {
72
    if (cur->stop[1] == '\0') {
72
        ret.kind = TK_NUL;
73
        ret.kind = TK_NUL;
73
        ret.start = cur->stop + 1;
74
        ret.start = cur->stop + 1;
74
        ret.stop = ret.start;
75
        ret.stop = ret.start;
75
        return ret;
76
        return ret;
76
    }
77
    }
77
    if (cur->stop[1] == '/') {
78
    if (cur->stop[1] == '/') {
78
        ret.kind = TK_SLASH;
79
        ret.kind = TK_SLASH;
79
        ret.start = cur->stop + 1;
80
        ret.start = cur->stop + 1;
80
        ret.stop = ret.start;
81
        ret.stop = ret.start;
81
        return ret;
82
        return ret;
82
    }
83
    }
83
    if (cur->stop[1] == '.' && (!cur->stop[2] || cur->stop[2] == '/')) {
84
    if (cur->stop[1] == '.' && (!cur->stop[2] || cur->stop[2] == '/')) {
84
        ret.kind = TK_DOT;
85
        ret.kind = TK_DOT;
85
        ret.start = cur->stop + 1;
86
        ret.start = cur->stop + 1;
86
        ret.stop = ret.start;
87
        ret.stop = ret.start;
87
        return ret;
88
        return ret;
88
    }
89
    }
89
    if (cur->stop[1] == '.' && cur->stop[2] == '.' &&
90
    if (cur->stop[1] == '.' && cur->stop[2] == '.' &&
90
        (!cur->stop[3] || cur->stop[3] == '/')) {
91
        (!cur->stop[3] || cur->stop[3] == '/')) {
91
        ret.kind = TK_DOTDOT;
92
        ret.kind = TK_DOTDOT;
92
        ret.start = cur->stop + 1;
93
        ret.start = cur->stop + 1;
93
        ret.stop = cur->stop + 2;
94
        ret.stop = cur->stop + 2;
94
        return ret;
95
        return ret;
95
    }
96
    }
96
    unsigned i;
97
    unsigned i;
97
    for (i = 1; cur->stop[i] && cur->stop[i] != '/'; i++)
98
    for (i = 1; cur->stop[i] && cur->stop[i] != '/'; i++)
98
        ;
99
        ;
99
    ret.kind = TK_COMP;
100
    ret.kind = TK_COMP;
100
    ret.start = &cur->stop[1];
101
    ret.start = &cur->stop[1];
101
    ret.stop = &cur->stop[i - 1];
102
    ret.stop = &cur->stop[i - 1];
102
    return ret;
103
    return ret;
103
}
104
}
104
 
105
 
105
/** States used by canonify(). */
106
/** States used by canonify(). */
106
typedef enum {
107
typedef enum {
107
    S_INI,
108
    S_INI,
108
    S_A,
109
    S_A,
109
    S_B,
110
    S_B,
110
    S_C,
111
    S_C,
111
    S_ACCEPT,
112
    S_ACCEPT,
112
    S_RESTART,
113
    S_RESTART,
113
    S_REJECT
114
    S_REJECT
114
} state_t;
115
} state_t;
115
 
116
 
116
typedef struct {
117
typedef struct {
117
    state_t s;
118
    state_t s;
118
    void (* f)(token_t *, token_t *, token_t *);
119
    void (* f)(token_t *, token_t *, token_t *);
119
} change_state_t;
120
} change_state_t;
120
 
121
 
121
/*
122
/*
122
 * Actions that can be performed when transitioning from one
123
 * Actions that can be performed when transitioning from one
123
 * state of canonify() to another.
124
 * state of canonify() to another.
124
 */
125
 */
125
static void set_first_slash(token_t *t, token_t *tfsl, token_t *tlcomp)
126
static void set_first_slash(token_t *t, token_t *tfsl, token_t *tlcomp)
126
{
127
{
127
    *tfsl = *t;
128
    *tfsl = *t;
-
 
129
    *tlcomp = *t;
128
}
130
}
129
static void save_component(token_t *t, token_t *tfsl, token_t *tlcomp)
131
static void save_component(token_t *t, token_t *tfsl, token_t *tlcomp)
130
{
132
{
131
    *tlcomp = *t;
133
    *tlcomp = *t;
132
}
134
}
133
static void terminate_slash(token_t *t, token_t *tfsl, token_t *tlcomp)
135
static void terminate_slash(token_t *t, token_t *tfsl, token_t *tlcomp)
134
{
136
{
135
    if (tfsl->stop[1])  /* avoid writing to a well-formatted path */
137
    if (tfsl->stop[1])  /* avoid writing to a well-formatted path */
136
        tfsl->stop[1] = '\0';
138
        tfsl->stop[1] = '\0';
137
}
139
}
138
static void remove_trailing_slash(token_t *t, token_t *tfsl, token_t *tlcomp)
140
static void remove_trailing_slash(token_t *t, token_t *tfsl, token_t *tlcomp)
139
{
141
{
140
    t->start[-1] = '\0';
142
    t->start[-1] = '\0';
141
}
143
}
142
/** Eat the extra '/'..
144
/** Eat the extra '/'..
143
 *
145
 *
144
 * @param t     The current TK_SLASH token.
146
 * @param t     The current TK_SLASH token.
145
 */
147
 */
146
static void shift_slash(token_t *t, token_t *tfsl, token_t *tlcomp)
148
static void shift_slash(token_t *t, token_t *tfsl, token_t *tlcomp)
147
{
149
{
148
    char *p = t->start;
150
    char *p = t->start;
149
    char *q = t->stop + 1;
151
    char *q = t->stop + 1;
150
    while ((*p++ = *q++))
152
    while ((*p++ = *q++))
151
        ;
153
        ;
152
}
154
}
153
/** Eat the extra '.'.
155
/** Eat the extra '.'.
154
 *
156
 *
155
 * @param t     The current TK_DOT token.
157
 * @param t     The current TK_DOT token.
156
 */
158
 */
157
static void shift_dot(token_t *t, token_t *tfsl, token_t *tlcomp)
159
static void shift_dot(token_t *t, token_t *tfsl, token_t *tlcomp)
158
{
160
{
159
    char *p = t->start;
161
    char *p = t->start;
160
    char *q = t->stop + 1;
162
    char *q = t->stop + 1;
161
    while ((*p++ = *q++))
163
    while ((*p++ = *q++))
162
        ;
164
        ;
163
}
165
}
164
/** Collapse the TK_COMP TK_SLASH TK_DOTDOT pattern.
166
/** Collapse the TK_COMP TK_SLASH TK_DOTDOT pattern.
165
 *
167
 *
166
 * @param t     The current TK_DOTDOT token.
168
 * @param t     The current TK_DOTDOT token.
167
 * @param tlcomp    The last TK_COMP token.
169
 * @param tlcomp    The last TK_COMP token.
168
 */
170
 */
169
static void shift_dotdot(token_t *t, token_t *tfsl, token_t *tlcomp)
171
static void shift_dotdot(token_t *t, token_t *tfsl, token_t *tlcomp)
170
{
172
{
171
    char *p = tlcomp->start;
173
    char *p = tlcomp->start;
172
    char *q = t->stop + 1;
174
    char *q = t->stop + 1;
173
    while ((*p++ = *q++))
175
    while ((*p++ = *q++))
174
        ;
176
        ;
175
}
177
}
176
 
178
 
177
/** Transition function for canonify(). */
179
/** Transition function for canonify(). */
178
static change_state_t trans[4][6] = {
180
static change_state_t trans[4][6] = {
179
    [S_INI] = {
181
    [S_INI] = {
180
        [TK_SLASH] = {
182
        [TK_SLASH] = {
181
            .s = S_A,
183
            .s = S_A,
182
            .f = set_first_slash,
184
            .f = set_first_slash,
183
        },
185
        },
184
        [TK_DOT] = {
186
        [TK_DOT] = {
185
            .s = S_REJECT,
187
            .s = S_REJECT,
186
            .f = NULL,
188
            .f = NULL,
187
        },
189
        },
188
        [TK_DOTDOT] = {
190
        [TK_DOTDOT] = {
189
            .s = S_REJECT,
191
            .s = S_REJECT,
190
            .f = NULL,
192
            .f = NULL,
191
        },
193
        },
192
        [TK_COMP] = {
194
        [TK_COMP] = {
193
            .s = S_REJECT,
195
            .s = S_REJECT,
194
            .f = NULL,
196
            .f = NULL,
195
        },
197
        },
196
        [TK_NUL] = {
198
        [TK_NUL] = {
197
            .s = S_REJECT,
199
            .s = S_REJECT,
198
            .f = NULL,
200
            .f = NULL,
199
        },
201
        },
200
        [TK_INVALID] = {
202
        [TK_INVALID] = {
201
            .s = S_REJECT,
203
            .s = S_REJECT,
202
            .f = NULL,
204
            .f = NULL,
203
        },
205
        },
204
    },
206
    },
205
    [S_A] = {
207
    [S_A] = {
206
        [TK_SLASH] = {
208
        [TK_SLASH] = {
207
            .s = S_A,
209
            .s = S_A,
208
            .f = set_first_slash,
210
            .f = set_first_slash,
209
        },
211
        },
210
        [TK_DOT] = {
212
        [TK_DOT] = {
211
            .s = S_A,
213
            .s = S_A,
212
            .f = NULL,
214
            .f = NULL,
213
        },
215
        },
214
        [TK_DOTDOT] = {
216
        [TK_DOTDOT] = {
215
            .s = S_A,
217
            .s = S_A,
216
            .f = NULL,
218
            .f = NULL,
217
        },
219
        },
218
        [TK_COMP] = {
220
        [TK_COMP] = {
219
            .s = S_B,
221
            .s = S_B,
220
            .f = save_component,
222
            .f = save_component,
221
        },
223
        },
222
        [TK_NUL] = {
224
        [TK_NUL] = {
223
            .s = S_ACCEPT,
225
            .s = S_ACCEPT,
224
            .f = terminate_slash,
226
            .f = terminate_slash,
225
        },
227
        },
226
        [TK_INVALID] = {
228
        [TK_INVALID] = {
227
            .s = S_REJECT,
229
            .s = S_REJECT,
228
            .f = NULL,
230
            .f = NULL,
229
        },
231
        },
230
    },
232
    },
231
    [S_B] = {
233
    [S_B] = {
232
        [TK_SLASH] = {
234
        [TK_SLASH] = {
233
            .s = S_C,
235
            .s = S_C,
234
            .f = NULL,
236
            .f = NULL,
235
        },
237
        },
236
        [TK_DOT] = {
238
        [TK_DOT] = {
237
            .s = S_REJECT,
239
            .s = S_REJECT,
238
            .f = NULL,
240
            .f = NULL,
239
        },
241
        },
240
        [TK_DOTDOT] = {
242
        [TK_DOTDOT] = {
241
            .s = S_REJECT,
243
            .s = S_REJECT,
242
            .f = NULL,
244
            .f = NULL,
243
        },
245
        },
244
        [TK_COMP] = {
246
        [TK_COMP] = {
245
            .s = S_REJECT,
247
            .s = S_REJECT,
246
            .f = NULL,
248
            .f = NULL,
247
        },
249
        },
248
        [TK_NUL] = {
250
        [TK_NUL] = {
249
            .s = S_ACCEPT,
251
            .s = S_ACCEPT,
250
            .f = NULL,
252
            .f = NULL,
251
        },
253
        },
252
        [TK_INVALID] = {
254
        [TK_INVALID] = {
253
            .s = S_REJECT,
255
            .s = S_REJECT,
254
            .f = NULL,
256
            .f = NULL,
255
        },
257
        },
256
    },
258
    },
257
    [S_C] = {
259
    [S_C] = {
258
        [TK_SLASH] = {
260
        [TK_SLASH] = {
259
            .s = S_RESTART,
261
            .s = S_RESTART,
260
            .f = shift_slash,
262
            .f = shift_slash,
261
        },
263
        },
262
        [TK_DOT] = {
264
        [TK_DOT] = {
263
            .s = S_RESTART,
265
            .s = S_RESTART,
264
            .f = shift_dot,
266
            .f = shift_dot,
265
        },
267
        },
266
        [TK_DOTDOT] = {
268
        [TK_DOTDOT] = {
267
            .s = S_RESTART,
269
            .s = S_RESTART,
268
            .f = shift_dotdot,
270
            .f = shift_dotdot,
269
        },
271
        },
270
        [TK_COMP] = {
272
        [TK_COMP] = {
271
            .s = S_B,
273
            .s = S_B,
272
            .f = save_component,
274
            .f = save_component,
273
        },
275
        },
274
        [TK_NUL] = {
276
        [TK_NUL] = {
275
            .s = S_ACCEPT,
277
            .s = S_ACCEPT,
276
            .f = remove_trailing_slash,
278
            .f = remove_trailing_slash,
277
        },
279
        },
278
        [TK_INVALID] = {
280
        [TK_INVALID] = {
279
            .s = S_REJECT,
281
            .s = S_REJECT,
280
            .f = NULL,
282
            .f = NULL,
281
        },
283
        },
282
    }
284
    }
283
};
285
};
284
 
286
 
285
/** Canonify a file system path.
287
/** Canonify a file system path.
286
 *
288
 *
287
 * A file system path is canonical, if the following holds:
289
 * A file system path is canonical, if the following holds:
288
 * 1) the path is absolute (i.e. a/b/c is not canonical)
290
 * 1) the path is absolute (i.e. a/b/c is not canonical)
289
 * 2) there is no trailing slash in the path (i.e. /a/b/c is not canonical)
291
 * 2) there is no trailing slash in the path (i.e. /a/b/c is not canonical)
290
 * 3) there is no extra slash in the path (i.e. /a//b/c is not canonical)
292
 * 3) there is no extra slash in the path (i.e. /a//b/c is not canonical)
291
 * 4) there is no '.' component in the path (i.e. /a/./b/c is not canonical)
293
 * 4) there is no '.' component in the path (i.e. /a/./b/c is not canonical)
292
 * 5) there is no '..' component in the path (i.e. /a/b/../c is not canonical)
294
 * 5) there is no '..' component in the path (i.e. /a/b/../c is not canonical)
293
 *
295
 *
294
 * This function makes a potentially non-canonical file system path canonical.
296
 * This function makes a potentially non-canonical file system path canonical.
295
 * It works in-place and requires a NULL-terminated input string.
297
 * It works in-place and requires a NULL-terminated input string.
296
 *
298
 *
297
 * @param path      Path to be canonified.
299
 * @param path      Path to be canonified.
298
 * @param lenp      Pointer where the length of the final path will be
300
 * @param lenp      Pointer where the length of the final path will be
299
 *          stored. Can be NULL.
301
 *          stored. Can be NULL.
300
 *
302
 *
301
 * @return      Canonified path or NULL on failure.
303
 * @return      Canonified path or NULL on failure.
302
 */
304
 */
303
char *canonify(char *path, size_t *lenp)
305
char *canonify(char *path, size_t *lenp)
304
{
306
{
305
    state_t state;
307
    state_t state;
306
    token_t t;
308
    token_t t;
307
    token_t tfsl;       /* first slash */
309
    token_t tfsl;       /* first slash */
308
    token_t tlcomp;     /* last component */
310
    token_t tlcomp;     /* last component */
309
    if (*path != '/')
311
    if (*path != '/')
310
        return NULL;
312
        return NULL;
311
    tfsl = slash_token(path);
313
    tfsl = slash_token(path);
312
restart:
314
restart:
313
    state = S_INI;
315
    state = S_INI;
314
    t = tfsl;
316
    t = tfsl;
315
    tlcomp = tfsl;
317
    tlcomp = tfsl;
316
    while (state != S_ACCEPT && state != S_RESTART && state != S_REJECT) {
318
    while (state != S_ACCEPT && state != S_RESTART && state != S_REJECT) {
317
        if (trans[state][t.kind].f)
319
        if (trans[state][t.kind].f)
318
            trans[state][t.kind].f(&t, &tfsl, &tlcomp);
320
            trans[state][t.kind].f(&t, &tfsl, &tlcomp);
319
        state = trans[state][t.kind].s;
321
        state = trans[state][t.kind].s;
320
        t = next_token(&t);
322
        t = next_token(&t);
321
    }
323
    }
322
   
324
   
323
    switch (state) {
325
    switch (state) {
324
    case S_RESTART:
326
    case S_RESTART:
325
        goto restart;
327
        goto restart;
326
    case S_REJECT:
328
    case S_REJECT:
327
        return NULL;
329
        return NULL;
328
    case S_ACCEPT:
330
    case S_ACCEPT:
329
        if (lenp)
331
        if (lenp)
330
            *lenp = (size_t)((tlcomp.stop - tfsl.start) + 1);
332
            *lenp = (size_t)((tlcomp.stop - tfsl.start) + 1);
331
        return tfsl.start;
333
        return tfsl.start;
332
    default:
334
    default:
333
        abort();
335
        abort();
334
    }
336
    }
335
}
337
}
336
 
338
 
337
/**
339
/**
338
 * @}
340
 * @}
339
 */
341
 */
340
 
342