Go to most recent revision | 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 |