source: branches/minix3-book/servers/fs/path.c

Last change on this file was 4, checked in by Mattia Monga, 14 years ago

Importazione sorgenti libro

File size: 12.4 KB
Line 
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 * The entry points into this file are
5 * eat_path: the 'main' routine of the path-to-inode conversion mechanism
6 * last_dir: find the final directory on a given path
7 * advance: parse one component of a path name
8 * search_dir: search a directory for a string and return its inode number
9 */
10
11#include "fs.h"
12#include <string.h>
13#include <minix/callnr.h>
14#include "buf.h"
15#include "file.h"
16#include "fproc.h"
17#include "inode.h"
18#include "super.h"
19
20PUBLIC char dot1[2] = "."; /* used for search_dir to bypass the access */
21PUBLIC char dot2[3] = ".."; /* permissions for . and .. */
22
23FORWARD _PROTOTYPE( char *get_name, (char *old_name, char string [NAME_MAX]) );
24
25/*===========================================================================*
26 * eat_path *
27 *===========================================================================*/
28PUBLIC struct inode *eat_path(path)
29char *path; /* the path name to be parsed */
30{
31/* Parse the path 'path' and put its inode in the inode table. If not possible,
32 * return NIL_INODE as function value and an error code in 'err_code'.
33 */
34
35 register struct inode *ldip, *rip;
36 char string[NAME_MAX]; /* hold 1 path component name here */
37
38 /* First open the path down to the final directory. */
39 if ( (ldip = last_dir(path, string)) == NIL_INODE) {
40 return(NIL_INODE); /* we couldn't open final directory */
41 }
42
43 /* The path consisting only of "/" is a special case, check for it. */
44 if (string[0] == '\0') return(ldip);
45
46 /* Get final component of the path. */
47 rip = advance(ldip, string);
48 put_inode(ldip);
49 return(rip);
50}
51
52/*===========================================================================*
53 * last_dir *
54 *===========================================================================*/
55PUBLIC struct inode *last_dir(path, string)
56char *path; /* the path name to be parsed */
57char string[NAME_MAX]; /* the final component is returned here */
58{
59/* Given a path, 'path', located in the fs address space, parse it as
60 * far as the last directory, fetch the inode for the last directory into
61 * the inode table, and return a pointer to the inode. In
62 * addition, return the final component of the path in 'string'.
63 * If the last directory can't be opened, return NIL_INODE and
64 * the reason for failure in 'err_code'.
65 */
66
67 register struct inode *rip;
68 register char *new_name;
69 register struct inode *new_ip;
70
71 /* Is the path absolute or relative? Initialize 'rip' accordingly. */
72 rip = (*path == '/' ? fp->fp_rootdir : fp->fp_workdir);
73
74 /* If dir has been removed or path is empty, return ENOENT. */
75 if (rip->i_nlinks == 0 || *path == '\0') {
76 err_code = ENOENT;
77 return(NIL_INODE);
78 }
79
80 dup_inode(rip); /* inode will be returned with put_inode */
81
82 /* Scan the path component by component. */
83 while (TRUE) {
84 /* Extract one component. */
85 if ( (new_name = get_name(path, string)) == (char*) 0) {
86 put_inode(rip); /* bad path in user space */
87 return(NIL_INODE);
88 }
89 if (*new_name == '\0') {
90 if ( (rip->i_mode & I_TYPE) == I_DIRECTORY) {
91 return(rip); /* normal exit */
92 } else {
93 /* last file of path prefix is not a directory */
94 put_inode(rip);
95 err_code = ENOTDIR;
96 return(NIL_INODE);
97 }
98 }
99
100 /* There is more path. Keep parsing. */
101 new_ip = advance(rip, string);
102 put_inode(rip); /* rip either obsolete or irrelevant */
103 if (new_ip == NIL_INODE) return(NIL_INODE);
104
105 /* The call to advance() succeeded. Fetch next component. */
106 path = new_name;
107 rip = new_ip;
108 }
109}
110
111/*===========================================================================*
112 * get_name *
113 *===========================================================================*/
114PRIVATE char *get_name(old_name, string)
115char *old_name; /* path name to parse */
116char string[NAME_MAX]; /* component extracted from 'old_name' */
117{
118/* Given a pointer to a path name in fs space, 'old_name', copy the next
119 * component to 'string' and pad with zeros. A pointer to that part of
120 * the name as yet unparsed is returned. Roughly speaking,
121 * 'get_name' = 'old_name' - 'string'.
122 *
123 * This routine follows the standard convention that /usr/ast, /usr//ast,
124 * //usr///ast and /usr/ast/ are all equivalent.
125 */
126
127 register int c;
128 register char *np, *rnp;
129
130 np = string; /* 'np' points to current position */
131 rnp = old_name; /* 'rnp' points to unparsed string */
132 while ( (c = *rnp) == '/') rnp++; /* skip leading slashes */
133
134 /* Copy the unparsed path, 'old_name', to the array, 'string'. */
135 while ( rnp < &old_name[PATH_MAX] && c != '/' && c != '\0') {
136 if (np < &string[NAME_MAX]) *np++ = c;
137 c = *++rnp; /* advance to next character */
138 }
139
140 /* To make /usr/ast/ equivalent to /usr/ast, skip trailing slashes. */
141 while (c == '/' && rnp < &old_name[PATH_MAX]) c = *++rnp;
142
143 if (np < &string[NAME_MAX]) *np = '\0'; /* Terminate string */
144
145 if (rnp >= &old_name[PATH_MAX]) {
146 err_code = ENAMETOOLONG;
147 return((char *) 0);
148 }
149 return(rnp);
150}
151
152/*===========================================================================*
153 * advance *
154 *===========================================================================*/
155PUBLIC struct inode *advance(dirp, string)
156struct inode *dirp; /* inode for directory to be searched */
157char string[NAME_MAX]; /* component name to look for */
158{
159/* Given a directory and a component of a path, look up the component in
160 * the directory, find the inode, open it, and return a pointer to its inode
161 * slot. If it can't be done, return NIL_INODE.
162 */
163
164 register struct inode *rip;
165 struct inode *rip2;
166 register struct super_block *sp;
167 int r, inumb;
168 dev_t mnt_dev;
169 ino_t numb;
170
171 /* If 'string' is empty, yield same inode straight away. */
172 if (string[0] == '\0') { return(get_inode(dirp->i_dev, (int) dirp->i_num)); }
173
174 /* Check for NIL_INODE. */
175 if (dirp == NIL_INODE) { return(NIL_INODE); }
176
177 /* If 'string' is not present in the directory, signal error. */
178 if ( (r = search_dir(dirp, string, &numb, LOOK_UP)) != OK) {
179 err_code = r;
180 return(NIL_INODE);
181 }
182
183 /* Don't go beyond the current root directory, unless the string is dot2. */
184 if (dirp == fp->fp_rootdir && strcmp(string, "..") == 0 && string != dot2)
185 return(get_inode(dirp->i_dev, (int) dirp->i_num));
186
187 /* The component has been found in the directory. Get inode. */
188 if ( (rip = get_inode(dirp->i_dev, (int) numb)) == NIL_INODE) {
189 return(NIL_INODE);
190 }
191
192 if (rip->i_num == ROOT_INODE)
193 if (dirp->i_num == ROOT_INODE) {
194 if (string[1] == '.') {
195 for (sp = &super_block[1]; sp < &super_block[NR_SUPERS]; sp++){
196 if (sp->s_dev == rip->i_dev) {
197 /* Release the root inode. Replace by the
198 * inode mounted on.
199 */
200 put_inode(rip);
201 mnt_dev = sp->s_imount->i_dev;
202 inumb = (int) sp->s_imount->i_num;
203 rip2 = get_inode(mnt_dev, inumb);
204 rip = advance(rip2, string);
205 put_inode(rip2);
206 break;
207 }
208 }
209 }
210 }
211 if (rip == NIL_INODE) return(NIL_INODE);
212
213 /* See if the inode is mounted on. If so, switch to root directory of the
214 * mounted file system. The super_block provides the linkage between the
215 * inode mounted on and the root directory of the mounted file system.
216 */
217 while (rip != NIL_INODE && rip->i_mount == I_MOUNT) {
218 /* The inode is indeed mounted on. */
219 for (sp = &super_block[0]; sp < &super_block[NR_SUPERS]; sp++) {
220 if (sp->s_imount == rip) {
221 /* Release the inode mounted on. Replace by the
222 * inode of the root inode of the mounted device.
223 */
224 put_inode(rip);
225 rip = get_inode(sp->s_dev, ROOT_INODE);
226 break;
227 }
228 }
229 }
230 return(rip); /* return pointer to inode's component */
231}
232
233/*===========================================================================*
234 * search_dir *
235 *===========================================================================*/
236PUBLIC int search_dir(ldir_ptr, string, numb, flag)
237register struct inode *ldir_ptr; /* ptr to inode for dir to search */
238char string[NAME_MAX]; /* component to search for */
239ino_t *numb; /* pointer to inode number */
240int flag; /* LOOK_UP, ENTER, DELETE or IS_EMPTY */
241{
242/* This function searches the directory whose inode is pointed to by 'ldip':
243 * if (flag == ENTER) enter 'string' in the directory with inode # '*numb';
244 * if (flag == DELETE) delete 'string' from the directory;
245 * if (flag == LOOK_UP) search for 'string' and return inode # in 'numb';
246 * if (flag == IS_EMPTY) return OK if only . and .. in dir else ENOTEMPTY;
247 *
248 * if 'string' is dot1 or dot2, no access permissions are checked.
249 */
250
251 register struct direct *dp = NULL;
252 register struct buf *bp = NULL;
253 int i, r, e_hit, t, match;
254 mode_t bits;
255 off_t pos;
256 unsigned new_slots, old_slots;
257 block_t b;
258 struct super_block *sp;
259 int extended = 0;
260
261 /* If 'ldir_ptr' is not a pointer to a dir inode, error. */
262 if ( (ldir_ptr->i_mode & I_TYPE) != I_DIRECTORY) return(ENOTDIR);
263
264 r = OK;
265
266 if (flag != IS_EMPTY) {
267 bits = (flag == LOOK_UP ? X_BIT : W_BIT | X_BIT);
268
269 if (string == dot1 || string == dot2) {
270 if (flag != LOOK_UP) r = read_only(ldir_ptr);
271 /* only a writable device is required. */
272 }
273 else r = forbidden(ldir_ptr, bits); /* check access permissions */
274 }
275 if (r != OK) return(r);
276
277 /* Step through the directory one block at a time. */
278 old_slots = (unsigned) (ldir_ptr->i_size/DIR_ENTRY_SIZE);
279 new_slots = 0;
280 e_hit = FALSE;
281 match = 0; /* set when a string match occurs */
282
283 for (pos = 0; pos < ldir_ptr->i_size; pos += ldir_ptr->i_sp->s_block_size) {
284 b = read_map(ldir_ptr, pos); /* get block number */
285
286 /* Since directories don't have holes, 'b' cannot be NO_BLOCK. */
287 bp = get_block(ldir_ptr->i_dev, b, NORMAL); /* get a dir block */
288
289 if (bp == NO_BLOCK)
290 panic(__FILE__,"get_block returned NO_BLOCK", NO_NUM);
291
292 /* Search a directory block. */
293 for (dp = &bp->b_dir[0];
294 dp < &bp->b_dir[NR_DIR_ENTRIES(ldir_ptr->i_sp->s_block_size)];
295 dp++) {
296 if (++new_slots > old_slots) { /* not found, but room left */
297 if (flag == ENTER) e_hit = TRUE;
298 break;
299 }
300
301 /* Match occurs if string found. */
302 if (flag != ENTER && 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 } else {
308 if (strncmp(dp->d_name, string, NAME_MAX) == 0) {
309 match = 1;
310 }
311 }
312 }
313
314 if (match) {
315 /* LOOK_UP or DELETE found what it wanted. */
316 r = OK;
317 if (flag == IS_EMPTY) r = ENOTEMPTY;
318 else if (flag == DELETE) {
319 /* Save d_ino for recovery. */
320 t = NAME_MAX - sizeof(ino_t);
321 *((ino_t *) &dp->d_name[t]) = dp->d_ino;
322 dp->d_ino = 0; /* erase entry */
323 bp->b_dirt = DIRTY;
324 ldir_ptr->i_update |= CTIME | MTIME;
325 ldir_ptr->i_dirt = DIRTY;
326 } else {
327 sp = ldir_ptr->i_sp; /* 'flag' is LOOK_UP */
328 *numb = conv4(sp->s_native, (int) dp->d_ino);
329 }
330 put_block(bp, DIRECTORY_BLOCK);
331 return(r);
332 }
333
334 /* Check for free slot for the benefit of ENTER. */
335 if (flag == ENTER && dp->d_ino == 0) {
336 e_hit = TRUE; /* we found a free slot */
337 break;
338 }
339 }
340
341 /* The whole block has been searched or ENTER has a free slot. */
342 if (e_hit) break; /* e_hit set if ENTER can be performed now */
343 put_block(bp, DIRECTORY_BLOCK); /* otherwise, continue searching dir */
344 }
345
346 /* The whole directory has now been searched. */
347 if (flag != ENTER) {
348 return(flag == IS_EMPTY ? OK : ENOENT);
349 }
350
351 /* This call is for ENTER. If no free slot has been found so far, try to
352 * extend directory.
353 */
354 if (e_hit == FALSE) { /* directory is full and no room left in last block */
355 new_slots++; /* increase directory size by 1 entry */
356 if (new_slots == 0) return(EFBIG); /* dir size limited by slot count */
357 if ( (bp = new_block(ldir_ptr, ldir_ptr->i_size)) == NIL_BUF)
358 return(err_code);
359 dp = &bp->b_dir[0];
360 extended = 1;
361 }
362
363 /* 'bp' now points to a directory block with space. 'dp' points to slot. */
364 (void) memset(dp->d_name, 0, (size_t) NAME_MAX); /* clear entry */
365 for (i = 0; string[i] && i < NAME_MAX; i++) dp->d_name[i] = string[i];
366 sp = ldir_ptr->i_sp;
367 dp->d_ino = conv4(sp->s_native, (int) *numb);
368 bp->b_dirt = DIRTY;
369 put_block(bp, DIRECTORY_BLOCK);
370 ldir_ptr->i_update |= CTIME | MTIME; /* mark mtime for update later */
371 ldir_ptr->i_dirt = DIRTY;
372 if (new_slots > old_slots) {
373 ldir_ptr->i_size = (off_t) new_slots * DIR_ENTRY_SIZE;
374 /* Send the change to disk if the directory is extended. */
375 if (extended) rw_inode(ldir_ptr, WRITING);
376 }
377 return(OK);
378}
Note: See TracBrowser for help on using the repository browser.