source: trunk/minix/servers/fs/path.c@ 20

Last change on this file since 20 was 9, checked in by Mattia Monga, 14 years ago

Minix 3.1.2a

File size: 16.6 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
12#include "fs.h"
13#include <string.h>
14#include <minix/callnr.h>
15#include <sys/stat.h>
16#include "buf.h"
17#include "file.h"
18#include "fproc.h"
19#include "inode.h"
20#include "super.h"
21
22PUBLIC char dot1[2] = "."; /* used for search_dir to bypass the access */
23PUBLIC char dot2[3] = ".."; /* permissions for . and .. */
24
25FORWARD _PROTOTYPE( char *get_name, (char *old_name, char string [NAME_MAX]) );
26
27FORWARD _PROTOTYPE( struct inode *ltraverse, (struct inode *rip,
28 char *path, char *suffix, struct inode *ldip) );
29
30/*===========================================================================*
31 * parse_path *
32 *===========================================================================*/
33PUBLIC struct inode *parse_path(path, string, action)
34char *path; /* the path name to be parsed */
35char string[NAME_MAX]; /* the final component is returned here */
36int action; /* action on last part of path */
37{
38/* This is the actual code for last_dir and eat_path. Return the inode of
39 * the last directory and the name of object within that directory, or the
40 * inode of the last object (an empty name will be returned). Names are
41 * returned in string. If string is null the name is discarded. The action
42 * code determines how "last" is defined. If an error occurs, NIL_INODE
43 * will be returned with an error code in err_code.
44 */
45
46 struct inode *rip, *dir_ip;
47 char *new_name;
48 int symloop;
49 char lstring[NAME_MAX];
50
51 /* Is the path absolute or relative? Initialize 'rip' accordingly. */
52 rip = (*path == '/' ? fp->fp_rootdir : fp->fp_workdir);
53
54 /* If dir has been removed or path is empty, return ENOENT. */
55 if (rip->i_nlinks == 0 || *path == '\0') {
56 err_code = ENOENT;
57 return(NIL_INODE);
58 }
59
60 dup_inode(rip); /* inode will be returned with put_inode */
61
62 symloop = 0; /* symbolic link traversal count */
63 if (string == (char *) 0) string = lstring;
64
65 /* Scan the path component by component. */
66 while (TRUE) {
67 /* Extract one component. */
68 if ( (new_name = get_name(path, string)) == (char*) 0) {
69 put_inode(rip); /* bad path in user space */
70 return(NIL_INODE);
71 }
72 if (*new_name == '\0' && (action & PATH_PENULTIMATE)) {
73 if ( (rip->i_mode & I_TYPE) == I_DIRECTORY) {
74 return(rip); /* normal exit */
75 } else {
76 /* last file of path prefix is not a directory */
77 put_inode(rip);
78 err_code = ENOTDIR;
79 return(NIL_INODE);
80 }
81 }
82
83 /* There is more path. Keep parsing. */
84 dir_ip = rip;
85 rip = advance(&dir_ip, string);
86
87 if (rip == NIL_INODE) {
88 if (*new_name == '\0' && (action & PATH_NONSYMBOLIC) != 0)
89 return(dir_ip);
90 else {
91 put_inode(dir_ip);
92 return(NIL_INODE);
93 }
94 }
95
96 /* The call to advance() succeeded. Fetch next component. */
97 if (S_ISLNK(rip->i_mode)) {
98 if (*new_name != '\0' || (action & PATH_OPAQUE) == 0) {
99 if (*new_name != '\0') new_name--;
100 rip = ltraverse(rip, path, new_name, dir_ip);
101 put_inode(dir_ip);
102 if (++symloop > SYMLOOP) {
103 err_code = ELOOP;
104 put_inode(rip);
105 rip = NIL_INODE;
106 }
107 if (rip == NIL_INODE) return(NIL_INODE);
108 continue;
109 }
110 } else if (*new_name != '\0') {
111 put_inode(dir_ip);
112 path = new_name;
113 continue;
114 }
115
116 /* Either last name reached or symbolic link is opaque */
117 if ((action & PATH_NONSYMBOLIC) != 0) {
118 put_inode(rip);
119 return(dir_ip);
120 } else {
121 put_inode(dir_ip);
122 return(rip);
123 }
124 }
125}
126
127/*===========================================================================*
128 * eat_path *
129 *===========================================================================*/
130PUBLIC struct inode *eat_path(path)
131char *path; /* the path name to be parsed */
132{
133 /* Parse the path 'path' and put its inode in the inode table. If not possible,
134 * return NIL_INODE as function value and an error code in 'err_code'.
135 */
136
137 return parse_path(path, (char *) 0, EAT_PATH);
138}
139
140/*===========================================================================*
141 * last_dir *
142 *===========================================================================*/
143PUBLIC struct inode *last_dir(path, string)
144char *path; /* the path name to be parsed */
145char string[NAME_MAX]; /* the final component is returned here */
146{
147/* Given a path, 'path', located in the fs address space, parse it as
148 * far as the last directory, fetch the inode for the last directory into
149 * the inode table, and return a pointer to the inode. In
150 * addition, return the final component of the path in 'string'.
151 * If the last directory can't be opened, return NIL_INODE and
152 * the reason for failure in 'err_code'.
153 */
154
155 return parse_path(path, string, LAST_DIR);
156}
157
158/*===========================================================================*
159 * ltraverse *
160 *===========================================================================*/
161PRIVATE struct inode *ltraverse(rip, path, suffix, ldip)
162register struct inode *rip; /* symbolic link */
163char *path; /* path containing link */
164char *suffix; /* suffix following link within path */
165register struct inode *ldip; /* directory containing link */
166{
167/* Traverse a symbolic link. Copy the link text from the inode and insert
168 * the text into the path. Return the inode of base directory and the
169 * ammended path. The symbolic link inode is always freed. The inode
170 * returned is already duplicated. NIL_INODE is returned on error.
171 */
172
173 block_t b; /* block containing link text */
174 struct inode *bip; /* inode of base directory */
175 struct buf *bp; /* buffer containing link text */
176 size_t sl; /* length of link */
177 size_t tl; /* length of suffix */
178 char *sp; /* start of link text */
179
180 bip = NIL_INODE;
181 bp = NIL_BUF;
182
183 if ((b = read_map(rip, (off_t) 0)) != NO_BLOCK) {
184 bp = get_block(rip->i_dev, b, NORMAL);
185 sl = rip->i_size;
186 sp = bp->b_data;
187
188 /* Insert symbolic text into path name. */
189 tl = strlen(suffix);
190 if (sl > 0 && sl + tl <= PATH_MAX-1) {
191 memmove(path+sl, suffix, tl);
192 memmove(path, sp, sl);
193 path[sl+tl] = 0;
194 dup_inode(bip = path[0] == '/' ? fp->fp_rootdir : ldip);
195 }
196 }
197
198 put_block(bp, DIRECTORY_BLOCK);
199 put_inode(rip);
200 if (bip == NIL_INODE)
201 {
202 err_code = ENOENT;
203 }
204 return (bip);
205}
206
207/*===========================================================================*
208 * get_name *
209 *===========================================================================*/
210PRIVATE char *get_name(old_name, string)
211char *old_name; /* path name to parse */
212char string[NAME_MAX]; /* component extracted from 'old_name' */
213{
214/* Given a pointer to a path name in fs space, 'old_name', copy the next
215 * component to 'string' and pad with zeros. A pointer to that part of
216 * the name as yet unparsed is returned. Roughly speaking,
217 * 'get_name' = 'old_name' - 'string'.
218 *
219 * This routine follows the standard convention that /usr/ast, /usr//ast,
220 * //usr///ast and /usr/ast/ are all equivalent.
221 */
222
223 register int c;
224 register char *np, *rnp;
225
226 np = string; /* 'np' points to current position */
227 rnp = old_name; /* 'rnp' points to unparsed string */
228 while ( (c = *rnp) == '/') rnp++; /* skip leading slashes */
229
230 /* Copy the unparsed path, 'old_name', to the array, 'string'. */
231 while ( rnp < &old_name[PATH_MAX] && c != '/' && c != '\0') {
232 if (np < &string[NAME_MAX]) *np++ = c;
233 c = *++rnp; /* advance to next character */
234 }
235
236 /* To make /usr/ast/ equivalent to /usr/ast, skip trailing slashes. */
237 while (c == '/' && rnp < &old_name[PATH_MAX]) c = *++rnp;
238
239 if (np < &string[NAME_MAX]) *np = '\0'; /* Terminate string */
240
241 if (rnp >= &old_name[PATH_MAX]) {
242 err_code = ENAMETOOLONG;
243 return((char *) 0);
244 }
245 return(rnp);
246}
247
248/*===========================================================================*
249 * advance *
250 *===========================================================================*/
251PUBLIC struct inode *advance(pdirp, string)
252struct inode **pdirp; /* inode for directory to be searched */
253char string[NAME_MAX]; /* component name to look for */
254{
255/* Given a directory and a component of a path, look up the component in
256 * the directory, find the inode, open it, and return a pointer to its inode
257 * slot. If it can't be done, return NIL_INODE.
258 */
259
260 register struct inode *rip, *dirp;
261 register struct super_block *sp;
262 int r, inumb;
263 dev_t mnt_dev;
264 ino_t numb;
265
266 dirp = *pdirp;
267
268 /* If 'string' is empty, yield same inode straight away. */
269 if (string[0] == '\0') { return(get_inode(dirp->i_dev, (int) dirp->i_num)); }
270
271 /* Check for NIL_INODE. */
272 if (dirp == NIL_INODE) { return(NIL_INODE); }
273
274 /* If 'string' is not present in the directory, signal error. */
275 if ( (r = search_dir(dirp, string, &numb, LOOK_UP)) != OK) {
276 err_code = r;
277 return(NIL_INODE);
278 }
279
280 /* Don't go beyond the current root directory, unless the string is dot2. */
281 if (dirp == fp->fp_rootdir && strcmp(string, "..") == 0 && string != dot2)
282 return(get_inode(dirp->i_dev, (int) dirp->i_num));
283
284 /* The component has been found in the directory. Get inode. */
285 if ( (rip = get_inode(dirp->i_dev, (int) numb)) == NIL_INODE) {
286 return(NIL_INODE);
287 }
288
289 /* The following test is for "mountpoint/.." where mountpoint is a
290 * mountpoint. ".." will refer to the root of the mounted filesystem,
291 * but has to become a reference to the parent of the 'mountpoint'
292 * directory.
293 *
294 * This case is recognized by the looked up name pointing to a
295 * root inode, and the directory in which it is held being a
296 * root inode, _and_ the name[1] being '.'. (This is a test for '..'
297 * and excludes '.'.)
298 */
299 if (rip->i_num == ROOT_INODE)
300 if (dirp->i_num == ROOT_INODE) {
301 if (string[1] == '.') {
302 sp= rip->i_sp;
303 if (sp->s_imount != sp->s_isup)
304 {
305 /* Release the root inode. Replace by the
306 * inode mounted on. Update parent.
307 */
308 put_inode(rip);
309 put_inode(dirp);
310 mnt_dev = sp->s_imount->i_dev;
311 inumb = (int) sp->s_imount->i_num;
312 dirp = *pdirp = get_inode(mnt_dev, inumb);
313 rip = advance(pdirp, string);
314 }
315 }
316 }
317 if (rip == NIL_INODE) return(NIL_INODE);
318
319 /* See if the inode is mounted on. If so, switch to root directory of the
320 * mounted file system. The super_block provides the linkage between the
321 * inode mounted on and the root directory of the mounted file system.
322 */
323 while (rip != NIL_INODE && rip->i_mount == I_MOUNT) {
324 /* The inode is indeed mounted on. */
325 for (sp = &super_block[0]; sp < &super_block[NR_SUPERS]; sp++) {
326 if (sp->s_imount == rip) {
327 /* Release the inode mounted on. Replace by the
328 * inode of the root inode of the mounted device.
329 */
330 put_inode(rip);
331 rip = get_inode(sp->s_dev, ROOT_INODE);
332 break;
333 }
334 }
335 }
336 return(rip); /* return pointer to inode's component */
337}
338
339/*===========================================================================*
340 * search_dir *
341 *===========================================================================*/
342PUBLIC int search_dir(ldir_ptr, string, numb, flag)
343register struct inode *ldir_ptr; /* ptr to inode for dir to search */
344char string[NAME_MAX]; /* component to search for */
345ino_t *numb; /* pointer to inode number */
346int flag; /* LOOK_UP, ENTER, DELETE or IS_EMPTY */
347{
348/* This function searches the directory whose inode is pointed to by 'ldip':
349 * if (flag == ENTER) enter 'string' in the directory with inode # '*numb';
350 * if (flag == DELETE) delete 'string' from the directory;
351 * if (flag == LOOK_UP) search for 'string' and return inode # in 'numb';
352 * if (flag == IS_EMPTY) return OK if only . and .. in dir else ENOTEMPTY;
353 *
354 * if 'string' is dot1 or dot2, no access permissions are checked.
355 */
356
357 register struct direct *dp = NULL;
358 register struct buf *bp = NULL;
359 int i, r, e_hit, t, match;
360 mode_t bits;
361 off_t pos;
362 unsigned new_slots, old_slots;
363 block_t b;
364 struct super_block *sp;
365 int extended = 0;
366
367 /* If 'ldir_ptr' is not a pointer to a dir inode, error. */
368 if ( (ldir_ptr->i_mode & I_TYPE) != I_DIRECTORY) {
369 return(ENOTDIR);
370 }
371
372 r = OK;
373
374 if (flag != IS_EMPTY) {
375 bits = (flag == LOOK_UP ? X_BIT : W_BIT | X_BIT);
376
377 if (string == dot1 || string == dot2) {
378 if (flag != LOOK_UP) r = read_only(ldir_ptr);
379 /* only a writable device is required. */
380 }
381 else r = forbidden(ldir_ptr, bits); /* check access permissions */
382 }
383 if (r != OK) return(r);
384
385 /* Step through the directory one block at a time. */
386 old_slots = (unsigned) (ldir_ptr->i_size/DIR_ENTRY_SIZE);
387 new_slots = 0;
388 e_hit = FALSE;
389 match = 0; /* set when a string match occurs */
390
391 for (pos = 0; pos < ldir_ptr->i_size; pos += ldir_ptr->i_sp->s_block_size) {
392 b = read_map(ldir_ptr, pos); /* get block number */
393
394 /* Since directories don't have holes, 'b' cannot be NO_BLOCK. */
395 bp = get_block(ldir_ptr->i_dev, b, NORMAL); /* get a dir block */
396
397 if (bp == NO_BLOCK)
398 panic(__FILE__,"get_block returned NO_BLOCK", NO_NUM);
399
400 /* Search a directory block. */
401 for (dp = &bp->b_dir[0];
402 dp < &bp->b_dir[NR_DIR_ENTRIES(ldir_ptr->i_sp->s_block_size)];
403 dp++) {
404 if (++new_slots > old_slots) { /* not found, but room left */
405 if (flag == ENTER) e_hit = TRUE;
406 break;
407 }
408
409 /* Match occurs if string found. */
410 if (flag != ENTER && dp->d_ino != 0) {
411 if (flag == IS_EMPTY) {
412 /* If this test succeeds, dir is not empty. */
413 if (strcmp(dp->d_name, "." ) != 0 &&
414 strcmp(dp->d_name, "..") != 0) match = 1;
415 } else {
416 if (strncmp(dp->d_name, string, NAME_MAX) == 0) {
417 match = 1;
418 }
419 }
420 }
421
422 if (match) {
423 /* LOOK_UP or DELETE found what it wanted. */
424 r = OK;
425 if (flag == IS_EMPTY) r = ENOTEMPTY;
426 else if (flag == DELETE) {
427 /* Save d_ino for recovery. */
428 t = NAME_MAX - sizeof(ino_t);
429 *((ino_t *) &dp->d_name[t]) = dp->d_ino;
430 dp->d_ino = 0; /* erase entry */
431 bp->b_dirt = DIRTY;
432 ldir_ptr->i_update |= CTIME | MTIME;
433 ldir_ptr->i_dirt = DIRTY;
434 } else {
435 sp = ldir_ptr->i_sp; /* 'flag' is LOOK_UP */
436 *numb = conv4(sp->s_native, (int) dp->d_ino);
437 }
438 put_block(bp, DIRECTORY_BLOCK);
439 return(r);
440 }
441
442 /* Check for free slot for the benefit of ENTER. */
443 if (flag == ENTER && dp->d_ino == 0) {
444 e_hit = TRUE; /* we found a free slot */
445 break;
446 }
447 }
448
449 /* The whole block has been searched or ENTER has a free slot. */
450 if (e_hit) break; /* e_hit set if ENTER can be performed now */
451 put_block(bp, DIRECTORY_BLOCK); /* otherwise, continue searching dir */
452 }
453
454 /* The whole directory has now been searched. */
455 if (flag != ENTER) {
456 return(flag == IS_EMPTY ? OK : ENOENT);
457 }
458
459 /* This call is for ENTER. If no free slot has been found so far, try to
460 * extend directory.
461 */
462 if (e_hit == FALSE) { /* directory is full and no room left in last block */
463 new_slots++; /* increase directory size by 1 entry */
464 if (new_slots == 0) return(EFBIG); /* dir size limited by slot count */
465 if ( (bp = new_block(ldir_ptr, ldir_ptr->i_size)) == NIL_BUF)
466 return(err_code);
467 dp = &bp->b_dir[0];
468 extended = 1;
469 }
470
471 /* 'bp' now points to a directory block with space. 'dp' points to slot. */
472 (void) memset(dp->d_name, 0, (size_t) NAME_MAX); /* clear entry */
473 for (i = 0; string[i] && i < NAME_MAX; i++) dp->d_name[i] = string[i];
474 sp = ldir_ptr->i_sp;
475 dp->d_ino = conv4(sp->s_native, (int) *numb);
476 bp->b_dirt = DIRTY;
477 put_block(bp, DIRECTORY_BLOCK);
478 ldir_ptr->i_update |= CTIME | MTIME; /* mark mtime for update later */
479 ldir_ptr->i_dirt = DIRTY;
480 if (new_slots > old_slots) {
481 ldir_ptr->i_size = (off_t) new_slots * DIR_ENTRY_SIZE;
482 /* Send the change to disk if the directory is extended. */
483 if (extended) rw_inode(ldir_ptr, WRITING);
484 }
485 return(OK);
486}
Note: See TracBrowser for help on using the repository browser.