secondary cache feature in vm.
[minix.git] / servers / mfs / path.c
blob531d249273bae03c447836ce609c41879f3b55ea
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.
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
12 #include "fs.h"
13 #include <string.h>
14 #include <minix/callnr.h>
15 #include <minix/endpoint.h>
16 #include <sys/stat.h>
17 #include <sys/types.h>
18 #include <unistd.h>
19 #include "buf.h"
20 #include "inode.h"
21 #include "super.h"
22 #include <minix/vfsif.h>
25 PUBLIC char dot1[2] = "."; /* used for search_dir to bypass the access */
26 PUBLIC char dot2[3] = ".."; /* permissions for . and .. */
28 FORWARD _PROTOTYPE( char *get_name, (char *name, char string[NAME_MAX+1]) );
29 FORWARD _PROTOTYPE( int ltraverse, (struct inode *rip, char *suffix) );
30 FORWARD _PROTOTYPE( int parse_path, (ino_t dir_ino, ino_t root_ino,
31 int flags, struct inode **res_inop,
32 size_t *offsetp, int *symlinkp) );
35 /*===========================================================================*
36 * fs_lookup *
37 *===========================================================================*/
38 PUBLIC int fs_lookup()
40 cp_grant_id_t grant, grant2;
41 int r, r1, len, flags, symlinks;
42 size_t offset = 0, path_size, cred_size;
43 ino_t dir_ino, root_ino;
44 struct inode *rip;
46 grant = fs_m_in.REQ_GRANT;
47 path_size = fs_m_in.REQ_PATH_SIZE; /* Size of the buffer */
48 len = fs_m_in.REQ_PATH_LEN; /* including terminating nul */
49 dir_ino = fs_m_in.REQ_DIR_INO;
50 root_ino = fs_m_in.REQ_ROOT_INO;
51 flags = fs_m_in.REQ_FLAGS;
53 /* Check length. */
54 if(len > sizeof(user_path)) return(E2BIG); /* too big for buffer */
55 if(len < 1) return(EINVAL); /* too small */
57 /* Copy the pathname and set up caller's user and group id */
58 r = sys_safecopyfrom(FS_PROC_NR, grant, /*offset*/ 0,
59 (vir_bytes) user_path, (phys_bytes) len, D);
60 if(r != OK) {
61 printf("MFS %s:%d sys_safecopyfrom failed: %d\n",
62 __FILE__, __LINE__, r);
63 return(r);
66 /* Verify this is a null-terminated path. */
67 if(user_path[len - 1] != '\0') return(EINVAL);
69 if(flags & PATH_GET_UCRED) { /* Do we have to copy uid/gid credentials? */
70 grant2 = fs_m_in.REQ_GRANT2;
71 cred_size = fs_m_in.REQ_UCRED_SIZE;
73 if (cred_size > sizeof(credentials)) return(EINVAL); /* Too big. */
74 r = sys_safecopyfrom(FS_PROC_NR, grant2, 0, (vir_bytes) &credentials,
75 (phys_bytes) cred_size, D);
76 if (r != OK) {
77 printf("MFS %s:%d sys_safecopyfrom failed: %d\n",
78 __FILE__, __LINE__, r);
79 return(r);
81 caller_uid = credentials.vu_uid;
82 caller_gid = credentials.vu_gid;
83 } else {
84 memset(&credentials, 0, sizeof(credentials));
85 caller_uid = fs_m_in.REQ_UID;
86 caller_gid = fs_m_in.REQ_GID;
89 /* Lookup inode */
90 rip = NULL;
91 r = parse_path(dir_ino, root_ino, flags, &rip, &offset, &symlinks);
93 if(symlinks != 0 && (r == ELEAVEMOUNT || r == EENTERMOUNT || r == ESYMLINK)){
94 len = strlen(user_path)+1;
95 if(len > path_size) return(ENAMETOOLONG);
97 r1 = sys_safecopyto(FS_PROC_NR, grant, 0, (vir_bytes) user_path,
98 (phys_bytes) len, D);
99 if(r1 != OK) {
100 printf("%s:%d fs_lookup: sys_safecopyto failed: %d\n",
101 __FILE__, __LINE__, r1);
102 return(r1);
106 if(r == ELEAVEMOUNT || r == ESYMLINK) {
107 /* Report offset and the error */
108 fs_m_out.RES_OFFSET = offset;
109 fs_m_out.RES_SYMLOOP = symlinks;
111 return(r);
114 if (r != OK && r != EENTERMOUNT) return(r);
116 fs_m_out.RES_INODE_NR = rip->i_num;
117 fs_m_out.RES_MODE = rip->i_mode;
118 fs_m_out.RES_FILE_SIZE_LO = rip->i_size;
119 fs_m_out.RES_SYMLOOP = symlinks;
120 fs_m_out.RES_UID = rip->i_uid;
121 fs_m_out.RES_GID = rip->i_gid;
123 /* This is only valid for block and character specials. But it doesn't
124 * cause any harm to set RES_DEV always. */
125 fs_m_out.RES_DEV = (dev_t) rip->i_zone[0];
127 if(r == EENTERMOUNT) {
128 fs_m_out.RES_OFFSET = offset;
129 put_inode(rip); /* Only return a reference to the final object */
132 return(r);
136 /*===========================================================================*
137 * parse_path *
138 *===========================================================================*/
139 PRIVATE int parse_path(dir_ino, root_ino, flags, res_inop, offsetp, symlinkp)
140 ino_t dir_ino;
141 ino_t root_ino;
142 int flags;
143 struct inode **res_inop;
144 size_t *offsetp;
145 int *symlinkp;
147 /* Parse the path in user_path, starting at dir_ino. If the path is the empty
148 * string, just return dir_ino. It is upto the caller to treat an empty
149 * path in a special way. Otherwise, if the path consists of just one or
150 * more slash ('/') characters, the path is replaced with ".". Otherwise,
151 * just look up the first (or only) component in path after skipping any
152 * leading slashes.
154 int r, leaving_mount;
155 struct inode *rip, *dir_ip;
156 char *cp, *next_cp; /* component and next component */
157 char component[NAME_MAX+1];
159 /* Start parsing path at the first component in user_path */
160 cp = user_path;
162 /* No symlinks encountered yet */
163 *symlinkp = 0;
165 /* Find starting inode inode according to the request message */
166 if((rip = find_inode(fs_dev, dir_ino)) == NIL_INODE)
167 return(ENOENT);
169 /* If dir has been removed return ENOENT. */
170 if (rip->i_nlinks == 0)
171 return(ENOENT);
173 dup_inode(rip);
175 /* If the given start inode is a mountpoint, we must be here because the file
176 * system mounted on top returned an ELEAVEMOUNT error. In this case, we must
177 * only accept ".." as the first path component.
179 leaving_mount = rip->i_mountpoint; /* True iff rip is a mountpoint */
181 /* Scan the path component by component. */
182 while (TRUE) {
183 if(cp[0] == '\0') {
184 /* We're done; either the path was empty or we've parsed all
185 components of the path */
187 *res_inop = rip;
188 *offsetp += cp - user_path;
190 /* Return EENTERMOUNT if we are at a mount point */
191 if (rip->i_mountpoint) return(EENTERMOUNT);
193 return(OK);
196 while(cp[0] == '/') cp++;
197 next_cp = get_name(cp, component);
199 /* Special code for '..'. A process is not allowed to leave a chrooted
200 * environment. A lookup of '..' at the root of a mounted filesystem
201 * has to return ELEAVEMOUNT. In both cases, the caller needs search
202 * permission for the current inode, as it is used as directory.
204 if(strcmp(component, "..") == 0) {
205 /* 'rip' is now accessed as directory */
206 if ((r = forbidden(rip, X_BIT)) != OK) {
207 put_inode(rip);
208 return(r);
211 if (rip->i_num == root_ino) {
212 cp = next_cp;
213 continue; /* Ignore the '..' at a process' root
214 and move on to the next component */
217 if (rip->i_num == ROOT_INODE && !rip->i_sp->s_is_root) {
218 /* Climbing up to parent FS */
220 put_inode(rip);
221 *offsetp += cp - user_path;
222 return(ELEAVEMOUNT);
226 /* Only check for a mount point if we are not coming from one. */
227 if (!leaving_mount && rip->i_mountpoint) {
228 /* Going to enter a child FS */
230 *res_inop = rip;
231 *offsetp += cp - user_path;
232 return(EENTERMOUNT);
235 /* There is more path. Keep parsing.
236 * If we're leaving a mountpoint, skip directory permission checks.
238 dir_ip = rip;
239 rip = advance(dir_ip, leaving_mount ? dot2 : component, CHK_PERM);
240 if(err_code == ELEAVEMOUNT || err_code == EENTERMOUNT)
241 err_code = OK;
243 if (err_code != OK) {
244 put_inode(dir_ip);
245 return(err_code);
248 leaving_mount = 0;
250 /* The call to advance() succeeded. Fetch next component. */
251 if (S_ISLNK(rip->i_mode)) {
253 if (next_cp[0] == '\0' && (flags & PATH_RET_SYMLINK)) {
254 put_inode(dir_ip);
255 *res_inop = rip;
256 *offsetp += next_cp - user_path;
257 return(OK);
260 /* Extract path name from the symlink file */
261 r = ltraverse(rip, next_cp);
262 next_cp = user_path;
263 *offsetp = 0;
265 /* Symloop limit reached? */
266 if (++(*symlinkp) > SYMLOOP_MAX)
267 r = ELOOP;
269 if (r != OK) {
270 put_inode(dir_ip);
271 put_inode(rip);
272 return(r);
275 if (next_cp[0] == '/') {
276 put_inode(dir_ip);
277 put_inode(rip);
278 return(ESYMLINK);
281 put_inode(rip);
282 dup_inode(dir_ip);
283 rip = dir_ip;
286 put_inode(dir_ip);
287 cp = next_cp; /* Process subsequent component in next round */
292 /*===========================================================================*
293 * ltraverse *
294 *===========================================================================*/
295 PRIVATE int ltraverse(rip, suffix)
296 register struct inode *rip; /* symbolic link */
297 char *suffix; /* current remaining path. Has to point in the
298 * user_path buffer
301 /* Traverse a symbolic link. Copy the link text from the inode and insert
302 * the text into the path. Return error code or report success. Base
303 * directory has to be determined according to the first character of the
304 * new pathname.
307 block_t blink; /* block containing link text */
308 size_t llen; /* length of link */
309 size_t slen; /* length of suffix */
310 struct buf *bp; /* buffer containing link text */
311 char *sp; /* start of link text */
313 bp = NIL_BUF;
315 if ((blink = read_map(rip, (off_t) 0)) == NO_BLOCK)
316 return(EIO);
318 bp = get_block(rip->i_dev, blink, NORMAL);
319 llen = rip->i_size;
320 sp = bp->b_data;
321 slen = strlen(suffix);
323 /* The path we're parsing looks like this:
324 * /already/processed/path/<link> or
325 * /already/processed/path/<link>/not/yet/processed/path
326 * After expanding the <link>, the path will look like
327 * <expandedlink> or
328 * <expandedlink>/not/yet/processed
329 * In both cases user_path must have enough room to hold <expandedlink>.
330 * However, in the latter case we have to move /not/yet/processed to the
331 * right place first, before we expand <link>. When strlen(<expandedlink>) is
332 * smaller than strlen(/already/processes/path), we move the suffix to the
333 * left. Is strlen(<expandedlink>) greater then we move it to the right. Else
334 * we do nothing. */
336 if (slen > 0) { /* Do we have path after the link? */
337 /* For simplicity we require that suffix starts with a slash */
338 if (suffix[0] != '/') {
339 panic("ltraverse: suffix does not start with a slash");
342 /* To be able to expand the <link>, we have to move the 'suffix'
343 * to the right place. */
344 if (slen + llen + 1 > sizeof(user_path))
345 return(ENAMETOOLONG);/* <expandedlink>+suffix+\0 does not fit*/
346 if (suffix-user_path != llen) /* Move suffix left or right if needed */
347 memmove(&user_path[llen], suffix, slen+1);
348 } else {
349 if (llen + 1 > sizeof(user_path))
350 return(ENAMETOOLONG); /* <expandedlink> + \0 does not fix */
352 /* Set terminating nul */
353 user_path[llen]= '\0';
356 /* Everything is set, now copy the expanded link to user_path */
357 memmove(user_path, sp, llen);
359 put_block(bp, DIRECTORY_BLOCK);
360 return(OK);
364 /*===========================================================================*
365 * advance *
366 *===========================================================================*/
367 PUBLIC struct inode *advance(dirp, string, chk_perm)
368 struct inode *dirp; /* inode for directory to be searched */
369 char string[NAME_MAX]; /* component name to look for */
370 int chk_perm; /* check permissions when string is looked up*/
372 /* Given a directory and a component of a path, look up the component in
373 * the directory, find the inode, open it, and return a pointer to its inode
374 * slot.
376 ino_t numb;
377 struct inode *rip;
379 /* If 'string' is empty, return an error. */
380 if (string[0] == '\0') {
381 err_code = ENOENT;
382 return(NIL_INODE);
385 /* Check for NIL_INODE. */
386 if (dirp == NIL_INODE) return(NIL_INODE);
388 /* If 'string' is not present in the directory, signal error. */
389 if ( (err_code = search_dir(dirp, string, &numb, LOOK_UP, chk_perm)) != OK) {
390 return(NIL_INODE);
393 /* The component has been found in the directory. Get inode. */
394 if ( (rip = get_inode(dirp->i_dev, (int) numb)) == NIL_INODE) {
395 return(NIL_INODE);
398 /* The following test is for "mountpoint/.." where mountpoint is a
399 * mountpoint. ".." will refer to the root of the mounted filesystem,
400 * but has to become a reference to the parent of the 'mountpoint'
401 * directory.
403 * This case is recognized by the looked up name pointing to a
404 * root inode, and the directory in which it is held being a
405 * root inode, _and_ the name[1] being '.'. (This is a test for '..'
406 * and excludes '.'.)
408 if (rip->i_num == ROOT_INODE) {
409 if (dirp->i_num == ROOT_INODE) {
410 if (string[1] == '.') {
411 if (!rip->i_sp->s_is_root) {
412 /* Climbing up mountpoint */
413 err_code = ELEAVEMOUNT;
419 /* See if the inode is mounted on. If so, switch to root directory of the
420 * mounted file system. The super_block provides the linkage between the
421 * inode mounted on and the root directory of the mounted file system.
423 if (rip != NIL_INODE && rip->i_mountpoint) {
424 /* Mountpoint encountered, report it */
425 err_code = EENTERMOUNT;
428 return(rip);
432 /*===========================================================================*
433 * get_name *
434 *===========================================================================*/
435 PRIVATE char *get_name(path_name, string)
436 char *path_name; /* path name to parse */
437 char string[NAME_MAX+1]; /* component extracted from 'old_name' */
439 /* Given a pointer to a path name in fs space, 'path_name', copy the first
440 * component to 'string' (truncated if necessary, always nul terminated).
441 * A pointer to the string after the first component of the name as yet
442 * unparsed is returned. Roughly speaking,
443 * 'get_name' = 'path_name' - 'string'.
445 * This routine follows the standard convention that /usr/ast, /usr//ast,
446 * //usr///ast and /usr/ast/ are all equivalent.
448 size_t len;
449 char *cp, *ep;
451 cp = path_name;
453 /* Skip leading slashes */
454 while (cp[0] == '/') cp++;
456 /* Find the end of the first component */
457 ep = cp;
458 while(ep[0] != '\0' && ep[0] != '/')
459 ep++;
461 len = ep - cp;
463 /* Truncate the amount to be copied if it exceeds NAME_MAX */
464 if (len > NAME_MAX)
465 len = NAME_MAX;
467 /* Special case of the string at cp is empty */
468 if (len == 0)
469 strcpy(string, "."); /* Return "." */
470 else {
471 memcpy(string, cp, len);
472 string[len]= '\0';
475 return(ep);
479 /*===========================================================================*
480 * search_dir *
481 *===========================================================================*/
482 PUBLIC int search_dir(ldir_ptr, string, numb, flag, check_permissions)
483 register struct inode *ldir_ptr; /* ptr to inode for dir to search */
484 char string[NAME_MAX]; /* component to search for */
485 ino_t *numb; /* pointer to inode number */
486 int flag; /* LOOK_UP, ENTER, DELETE or IS_EMPTY */
487 int check_permissions; /* check permissions when flag is !IS_EMPTY */
489 /* This function searches the directory whose inode is pointed to by 'ldip':
490 * if (flag == ENTER) enter 'string' in the directory with inode # '*numb';
491 * if (flag == DELETE) delete 'string' from the directory;
492 * if (flag == LOOK_UP) search for 'string' and return inode # in 'numb';
493 * if (flag == IS_EMPTY) return OK if only . and .. in dir else ENOTEMPTY;
495 * if 'string' is dot1 or dot2, no access permissions are checked.
498 register struct direct *dp = NULL;
499 register struct buf *bp = NULL;
500 int i, r, e_hit, t, match;
501 mode_t bits;
502 off_t pos;
503 unsigned new_slots, old_slots;
504 block_t b;
505 struct super_block *sp;
506 int extended = 0;
508 /* If 'ldir_ptr' is not a pointer to a dir inode, error. */
509 if ( (ldir_ptr->i_mode & I_TYPE) != I_DIRECTORY) {
510 return(ENOTDIR);
513 r = OK;
515 if (flag != IS_EMPTY) {
516 bits = (flag == LOOK_UP ? X_BIT : W_BIT | X_BIT);
518 if (string == dot1 || string == dot2) {
519 if (flag != LOOK_UP) r = read_only(ldir_ptr);
520 /* only a writable device is required. */
521 } else if(check_permissions) {
522 r = forbidden(ldir_ptr, bits); /* check access permissions */
525 if (r != OK) return(r);
527 /* Step through the directory one block at a time. */
528 old_slots = (unsigned) (ldir_ptr->i_size/DIR_ENTRY_SIZE);
529 new_slots = 0;
530 e_hit = FALSE;
531 match = 0; /* set when a string match occurs */
533 for (pos = 0; pos < ldir_ptr->i_size; pos += ldir_ptr->i_sp->s_block_size) {
534 b = read_map(ldir_ptr, pos); /* get block number */
536 /* Since directories don't have holes, 'b' cannot be NO_BLOCK. */
537 bp = get_block(ldir_ptr->i_dev, b, NORMAL); /* get a dir block */
539 if (bp == NO_BLOCK)
540 panic("get_block returned NO_BLOCK");
542 /* Search a directory block. */
543 for (dp = &bp->b_dir[0];
544 dp < &bp->b_dir[NR_DIR_ENTRIES(ldir_ptr->i_sp->s_block_size)];
545 dp++) {
546 if (++new_slots > old_slots) { /* not found, but room left */
547 if (flag == ENTER) e_hit = TRUE;
548 break;
551 /* Match occurs if string found. */
552 if (flag != ENTER && dp->d_ino != 0) {
553 if (flag == IS_EMPTY) {
554 /* If this test succeeds, dir is not empty. */
555 if (strcmp(dp->d_name, "." ) != 0 &&
556 strcmp(dp->d_name, "..") != 0) match = 1;
557 } else {
558 if (strncmp(dp->d_name, string, NAME_MAX) == 0){
559 match = 1;
564 if (match) {
565 /* LOOK_UP or DELETE found what it wanted. */
566 r = OK;
567 if (flag == IS_EMPTY) r = ENOTEMPTY;
568 else if (flag == DELETE) {
569 /* Save d_ino for recovery. */
570 t = NAME_MAX - sizeof(ino_t);
571 *((ino_t *) &dp->d_name[t]) = dp->d_ino;
572 dp->d_ino = 0; /* erase entry */
573 bp->b_dirt = DIRTY;
574 ldir_ptr->i_update |= CTIME | MTIME;
575 ldir_ptr->i_dirt = DIRTY;
576 } else {
577 sp = ldir_ptr->i_sp; /* 'flag' is LOOK_UP */
578 *numb = conv4(sp->s_native, (int) dp->d_ino);
580 put_block(bp, DIRECTORY_BLOCK);
581 return(r);
584 /* Check for free slot for the benefit of ENTER. */
585 if (flag == ENTER && dp->d_ino == 0) {
586 e_hit = TRUE; /* we found a free slot */
587 break;
591 /* The whole block has been searched or ENTER has a free slot. */
592 if (e_hit) break; /* e_hit set if ENTER can be performed now */
593 put_block(bp, DIRECTORY_BLOCK); /* otherwise, continue searching dir */
596 /* The whole directory has now been searched. */
597 if (flag != ENTER) {
598 return(flag == IS_EMPTY ? OK : ENOENT);
601 /* This call is for ENTER. If no free slot has been found so far, try to
602 * extend directory.
604 if (e_hit == FALSE) { /* directory is full and no room left in last block */
605 new_slots++; /* increase directory size by 1 entry */
606 if (new_slots == 0) return(EFBIG); /* dir size limited by slot count */
607 if ( (bp = new_block(ldir_ptr, ldir_ptr->i_size)) == NIL_BUF)
608 return(err_code);
609 dp = &bp->b_dir[0];
610 extended = 1;
613 /* 'bp' now points to a directory block with space. 'dp' points to slot. */
614 (void) memset(dp->d_name, 0, (size_t) NAME_MAX); /* clear entry */
615 for (i = 0; i < NAME_MAX && string[i]; i++) dp->d_name[i] = string[i];
616 sp = ldir_ptr->i_sp;
617 dp->d_ino = conv4(sp->s_native, (int) *numb);
618 bp->b_dirt = DIRTY;
619 put_block(bp, DIRECTORY_BLOCK);
620 ldir_ptr->i_update |= CTIME | MTIME; /* mark mtime for update later */
621 ldir_ptr->i_dirt = DIRTY;
622 if (new_slots > old_slots) {
623 ldir_ptr->i_size = (off_t) new_slots * DIR_ENTRY_SIZE;
624 /* Send the change to disk if the directory is extended. */
625 if (extended) rw_inode(ldir_ptr, WRITING);
627 return(OK);