Subversion Repositories HelenOS

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2376 konopa 1
/* This file contains the procedures that look up path names in the directory
2
* system and determine the inode number that goes with a given path name.
3
*/
4
 
5
/* Methods:
6
 * eat_path:        the 'main' routine of the path-to-inode conversion mechanism
7
 * last_dir:        find the final directory on a given path
8
 * advance:     parse one component of a path name
9
 * search_dir:      search a directory for a string and return its inode number
10
 * search_dir_ex:   used for extended versions
11
 */
12
 
13
 
14
#include <string.h>
15
#include "fs.h"
16
#include "block.h"
17
#include "file.h"
18
#include "fproc.h"
19
#include "inode.h"
20
#include "super.h"
21
 
22
 
23
static char *get_name(char *old_name, char *string, int string_length);
24
 
25
inode_t *eat_path(char *path)
26
{
27
 
28
    /* Parse the path 'path' and put its inode in the inode table. If not possible,
29
     * return NIL_INODE as function value and an error code in 'err_code'.
30
     */
31
 
32
    register inode_t *ldip, *rip;
33
    super_block_t *sp;
34
    int name_len;
35
    char string[name_len];        /* hold 1 path component name here */
36
 
37
    name_len = NAME_MAX;
38
    if ((sp = get_super()) == NIL_SUPER)
39
        return NIL_INODE;
40
 
41
    if (sp->s_extend) {
42
        name_len = NAME_MAX_EX;
43
    }
44
 
45
    /* First open the path down to the final directory. */
46
    if ( (ldip = last_dir(path, string, name_len)) == NIL_INODE)
47
        return NIL_INODE;      /* we couldn't open final directory */
48
 
49
    /* The path consisting only of "/" is a special case, check for it. */
50
    if (string[0] == '\0')
51
        return ldip;
52
 
53
    /* Get final component of the path. */
54
    rip = advance(ldip, string, name_len);
55
    put_inode(ldip);
56
 
57
    return rip;
58
}
59
 
60
inode_t *last_dir(char *path, char *string, int string_length)
61
{
62
 
63
    /* Given a path, 'path', located in the fs address space, parse it as
64
     * far as the last directory, fetch the inode for the last directory into
65
     * the inode table, and return a pointer to the inode.  In
66
     * addition, return the final component of the path in 'string'.
67
     * If the last directory can't be opened, return NIL_INODE and
68
     * the reason for failure in 'err_code'.
69
     */
70
 
71
    register inode_t *rip;
72
    register char *new_name;
73
    register inode_t *new_ip;
74
 
75
    /* Is the path absolute or relative?  Initialize 'rip' accordingly. */
76
    rip = (*path == '/' ? fp->fp_rootdir : fp->fp_workdir);
77
 
78
    /* If dir has been removed or path is empty, return FS_ENOENT. */
79
    if (rip->i_nlinks == 0  ||  *path == '\0') {
80
        err_code = FS_ENOENT;
81
        return NIL_INODE;
82
    }
83
 
84
    dup_inode(rip);     /* inode will be returned with put_inode */
85
 
86
    /* Scan the path component by component. */
87
    while (TRUE) {
88
        /* Extract one component. */
89
        if ( (new_name = get_name(path, string, string_length)) == (char*) 0) {
90
            put_inode(rip); /* bad path in user space */
91
            return NIL_INODE;
92
            }
93
        if (*new_name == '\0')
94
        {
95
            if ( (rip->i_mode & I_TYPE) == I_DIRECTORY)
96
                return rip;    /* normal exit */
97
            else {
98
                /* last file of path prefix is not a directory */
99
                put_inode(rip);
100
                err_code = FS_ENOTDIR;         
101
                return NIL_INODE;
102
            }
103
        }
104
            /* There is more path.  Keep parsing. */
105
        new_ip = advance(rip, string, string_length);
106
        put_inode(rip);
107
 
108
        if (new_ip == NIL_INODE) {
109
            return NIL_INODE;
110
        }
111
        /* The call to advance() succeeded.  Fetch next component. */
112
        path = new_name;
113
        rip = new_ip;
114
    }
115
}
116
 
117
char *get_name(char *old_name, char *string, int string_length)
118
{
119
 
120
    /* Given a pointer to a path name in fs space, 'old_name', copy the next
121
     * component to 'string' and pad with zeros.  A pointer to that part of
122
     * the name as yet unparsed is returned.  Roughly speaking,
123
     * 'get_name' = 'old_name' - 'string'.
124
     *
125
     * This routine follows the standard convention that /usr/ast, /usr//ast,
126
     * //usr///ast and /usr/ast/ are all equivalent.
127
     */
128
 
129
    register int c;
130
    register char *np, *rnp;
131
 
132
    np = string;                  /* 'np' points to current position */
133
    rnp = old_name;               /* 'rnp' points to unparsed string */
134
    while ((c = *rnp) == '/')
135
        rnp++;     /* skip leading slashes */
136
 
137
    /* Copy the unparsed path, 'old_name', to the array, 'string'. */
138
    while ( rnp < &old_name[PATH_MAX]  &&  c != '/'   &&  c != '\0') {
139
        if (np < &string[string_length])
140
            *np++ = c;
141
 
142
        c = *++rnp;             /* advance to next character */
143
    }
144
 
145
    /* To make /usr/ast/ equivalent to /usr/ast, skip trailing slashes. */
146
    while (c == '/' && rnp < &old_name[PATH_MAX])
147
        c = *++rnp;
148
 
149
    /* Padding with zeroes */
150
    if (np < &string[string_length])
151
        *np = '\0';       /* Terminate string */
152
 
153
    if (rnp >= &old_name[PATH_MAX]) {
154
        err_code = FS_ENAMETOOLONG;
155
        return((char *) 0);
156
    }
157
 
158
    return rnp;
159
}
160
 
161
inode_t *advance(inode_t *dirp, char *string, int string_length)
162
{
163
 
164
    /* Given a directory and a component of a path, look up the component in
165
     * the directory, find the inode, open it, and return a pointer to its inode
166
     * slot.  If it can't be done, return NIL_INODE.
167
     */
168
 
169
    register inode_t *rip;
170
    int r;
171
    ino_t numb;
172
 
173
    /* If 'string' is empty, yield same inode straight away. */
174
    if (string[0] == '\0')
175
        return get_inode((int) dirp->i_num);
176
 
177
    /* Check for NIL_INODE. */
178
    if (dirp == NIL_INODE)
179
        return NIL_INODE;
180
 
181
    /* If 'string' is not present in the directory, signal error. */
182
    if (dirp->i_sp->s_extend) {
183
        r = search_dir_ex(dirp, string, &numb, LOOK_UP);
184
    }
185
    else {
186
        r = search_dir(dirp, string, &numb, LOOK_UP);
187
    }
188
    if (r != OK) {
189
        err_code = r;
190
        return NIL_INODE;
191
    }
192
 
193
    /* Don't go beyond the current root directory */
194
    if (dirp == fp->fp_rootdir && strcmp(string, "..") == 0)
195
        return(get_inode((int) dirp->i_num));
196
 
197
    /* The component has been found in the directory.  Get inode. */
198
    if ( (rip = get_inode((int) numb)) == NIL_INODE)
199
        return NIL_INODE;
200
 
201
    return rip;          /* return pointer to inode's component */
202
}
203
 
204
int search_dir(register inode_t *ldir_ptr, char string[NAME_MAX], ino_t *numb, int flag)
205
{
206
 
207
    /* This function searches the directory whose inode is pointed to by 'ldip':
208
     * if (flag == LOOK_UP) search for 'string' and return inode # in 'numb';
209
     * if (flag == IS_EMPTY) return OK if only . and .. in dir else ENOTEMPTY;
210
     */
211
 
212
    register direct_t *dp;
213
    register block_t *bp;
214
    int r, match;
215
    offset_t pos;
216
    unsigned old_slots;
217
    block_num_t b;
218
 
219
    /* If 'ldir_ptr' is not a pointer to a dir inode, error. */
220
    if ((ldir_ptr->i_mode & I_TYPE) != I_DIRECTORY)
221
        return FS_ENOTDIR;
222
 
223
    r = OK;
224
 
225
    /* Step through the directory one block at a time. */
226
    old_slots = (unsigned)(ldir_ptr->i_size/DIR_ENTRY_SIZE);  
227
    match = 0;                    /* set when a string match occurs */
228
 
229
    for (pos = 0; pos < ldir_ptr->i_size; pos += BLOCK_SIZE) {
230
        b = read_map(ldir_ptr, pos);    /* get block number */
231
 
232
        /* Since directories don't have holes, 'b' cannot be NO_BLOCK. */
233
        bp = get_block(b);     /* get a dir block */
234
 
235
        /* Search a directory block. */
236
        for (dp = &bp->b.b__dir[0]; dp < &bp->b.b__dir[NR_DIR_ENTRIES]; dp++) {
237
 
238
                    /* Match occurs if string found. */
239
                    if (dp->d_ino != 0) {
240
                if (flag == IS_EMPTY) {
241
                    /* If this test succeeds, dir is not empty. */
242
                    if (strcmp(dp->d_name, "." ) != 0 &&
243
                        strcmp(dp->d_name, "..") != 0)
244
                        match = 1;
245
                            }
246
                else {
247
                    if (fs_strncmp(dp->d_name, string, NAME_MAX) == 0)
248
                        match = 1;
249
                }
250
            }
251
 
252
            if (match) {
253
                /* LOOK_UP or DELETE found what it wanted. */
254
                r = OK;
255
                if (flag == IS_EMPTY) {
256
                    r = FS_ENOTEMPTY;
257
                }
258
                else {
259
                    *numb =  (int) dp->d_ino;
260
                }
261
                return r;
262
            }
263
        }  
264
    }
265
 
266
    /* The whole directory has now been searched. */
267
    return(flag == IS_EMPTY ? OK : FS_ENOENT); 
268
}
269
 
270
int search_dir_ex(register inode_t *ldir_ptr, char string[NAME_MAX_EX], ino_t *numb, int flag)
271
{
272
 
273
    /* Same as above, but for extened directory etries - 30 chars in name of file. */
274
 
275
    register directex_t *dp;
276
    register block_t *bp;
277
    int r, match;
278
    offset_t pos;
279
    unsigned old_slots;
280
    block_num_t b;
281
 
282
    /* If 'ldir_ptr' is not a pointer to a dir inode, error. */
283
    if ( (ldir_ptr->i_mode & I_TYPE) != I_DIRECTORY)
284
        return FS_ENOTDIR;
285
 
286
    r = OK;
287
 
288
    /* Step through the directory one block at a time. */
289
    old_slots = (unsigned)(ldir_ptr->i_size/DIR_ENTRY_SIZE_EX);
290
    match = 0;                    /* set when a string match occurs */
291
 
292
    for (pos = 0; pos < ldir_ptr->i_size; pos += BLOCK_SIZE) {
293
        b = read_map(ldir_ptr, pos);    /* get block number */
294
 
295
        /* Since directories don't have holes, 'b' cannot be NO_BLOCK. */
296
        bp = get_block(b);     /* get a dir block */
297
 
298
        /* Search a directory block. */
299
        for (dp = &bp->b.b__direx[0]; dp < &bp->b.b__direx[NR_DIR_ENTRIES_EX]; dp++) {
300
 
301
            /* Match occurs if string found. */
302
            if (dp->d_ino != 0) {
303
                if (flag == IS_EMPTY) {
304
                /* If this test succeeds, dir is not empty. */
305
                    if (strcmp(dp->d_name, "." ) != 0 &&
306
                        strcmp(dp->d_name, "..") != 0) match = 1;
307
                            }
308
                else {
309
                    if (fs_strncmp(dp->d_name, string, NAME_MAX_EX) == 0)
310
                        match = 1;
311
                }
312
            }
313
 
314
            if (match) {
315
                /* LOOK_UP or DELETE found what it wanted. */
316
                r = OK;
317
                if (flag == IS_EMPTY) {
318
                    r = FS_ENOTEMPTY;
319
                }
320
                else {
321
                    *numb =  (int)dp->d_ino;
322
                }
323
                return r;
324
            }
325
        }        
326
    }
327
 
328
    /* The whole directory has now been searched. */
329
    return(flag == IS_EMPTY ? OK : FS_ENOENT); 
330
}
331