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
14 #include <minix/callnr.h>
15 #include <minix/endpoint.h>
21 #include <minix/vfsif.h>
24 PUBLIC
char dot1
[2] = "."; /* used for search_dir to bypass the access */
25 PUBLIC
char dot2
[3] = ".."; /* permissions for . and .. */
27 FORWARD
_PROTOTYPE( char *get_name
, (char *old_name
, char string
[NAME_MAX
]) );
28 FORWARD
_PROTOTYPE( int ltraverse
, (struct inode
*rip
, char *path
,
29 char *suffix
, int pathlen
) );
32 /*===========================================================================*
34 *===========================================================================*/
37 char string
[PATH_MAX
];
45 len
= fs_m_in
.REQ_PATH_LEN
;
46 if(len
> sizeof(user_path
)) return E2BIG
; /* too big for buffer */
47 if(len
< 1) return EINVAL
; /* too small for \0 */
49 /* Copy the pathname and set up caller's user and group id */
50 err_code
= sys_datacopy(FS_PROC_NR
, (vir_bytes
) fs_m_in
.REQ_PATH
, SELF
,
51 (vir_bytes
) user_path
, (phys_bytes
) len
);
53 printf("mfs:%s:%d: sys_datacopy failed: %d\n", __FILE__
, __LINE__
, err_code
);
57 /* Verify this is a null-terminated path. */
58 if(user_path
[len
-1] != '\0') {
59 printf("mfs:lookup: didn't get null-terminated string.\n");
63 caller_uid
= fs_m_in
.REQ_UID
;
64 caller_gid
= fs_m_in
.REQ_GID
;
65 flags
= fs_m_in
.REQ_FLAGS
;
67 /* Clear RES_OFFSET for ENOENT */
68 fs_m_out
.RES_OFFSET
= 0;
71 rip
= parse_path(user_path
, string
, flags
);
73 /* Copy back the last name if it is required */
74 if (err_code
!= OK
|| (flags
& PATH_PENULTIMATE
)) {
75 s_error
= sys_datacopy(SELF_E
, (vir_bytes
) string
, FS_PROC_NR
,
76 (vir_bytes
) fs_m_in
.REQ_USER_ADDR
, (phys_bytes
) NAME_MAX
);
78 printf("mfs:%s:%d: sys_datacopy failed: %d\n",
79 __FILE__
, __LINE__
, s_error
);
84 /* Error or mount point encountered */
87 if (err_code
!= EENTERMOUNT
)
88 fs_m_out
.RES_INODE_NR
= 0; /* signal no inode */
92 fs_m_out
.RES_INODE_NR
= rip
->i_num
;
93 fs_m_out
.RES_MODE
= rip
->i_mode
;
94 fs_m_out
.RES_FILE_SIZE
= rip
->i_size
;
96 /* If 'path' is a block special file, return dev number. */
97 if ( (rip
->i_mode
& I_TYPE
) == I_BLOCK_SPECIAL
) {
98 fs_m_out
.RES_DEV
= (dev_t
) rip
->i_zone
[0];
101 /* Drop inode (path parse increased the counter) */
108 /*===========================================================================*
110 *===========================================================================*/
111 PUBLIC
struct inode
*parse_path(path
, string
, action
)
112 char *path
; /* the path name to be parsed */
113 char string
[NAME_MAX
]; /* the final component is returned here */
114 int action
; /* action on last part of path */
116 /* This is the actual code for last_dir and eat_path. Return the inode of
117 * the last directory and the name of object within that directory, or the
118 * inode of the last object (an empty name will be returned). Names are
119 * returned in string. If string is null the name is discarded. The action
120 * code determines how "last" is defined. If an error occurs, NIL_INODE
121 * will be returned with an error code in err_code.
124 struct inode
*rip
, *dir_ip
;
125 struct inode
*ver_rip
;
127 char lstring
[NAME_MAX
];
129 /* Find starting inode inode according to the request message */
130 if ((rip
= find_inode(fs_dev
, fs_m_in
.REQ_INODE_NR
)) == NIL_INODE
) {
131 printf("FS: couldn't find starting inode req_nr: %d %s\n", req_nr
,
134 printf("%s, %d\n", __FILE__
, __LINE__
);
138 /* Find chroot inode according to the request message */
139 if (fs_m_in
.REQ_CHROOT_NR
!= 0) {
140 if ((chroot_dir
= find_inode(fs_dev
, fs_m_in
.REQ_CHROOT_NR
))
142 printf("FS: couldn't find chroot inode\n");
144 printf("%s, %d\n", __FILE__
, __LINE__
);
148 else chroot_dir
= NIL_INODE
;
150 /* Set user and group ID */
151 caller_uid
= fs_m_in
.REQ_UID
;
152 caller_gid
= fs_m_in
.REQ_GID
;
154 /* No characters were processed yet */
157 /* Current number of symlinks encountered */
158 symloop
= fs_m_in
.REQ_SYMLOOP
;
160 /* If dir has been removed return ENOENT. */
161 /* Note: empty (start) path is checked in the VFS process */
162 if (rip
->i_nlinks
== 0/* || *path == '\0'*/) {
164 printf("%s, %d\n", __FILE__
, __LINE__
);
168 /* There is only one way how the starting directory of the lookup
169 * can be a mount point which is not a root directory,
170 * namely: climbing up on a mount (ELEAVEMOUNT).
171 * In this case the lookup is intrested in the parent dir of the mount
172 * point, but the last ".." component was processed in the 'previous'
173 * FS process. Let's do that first.
175 if (rip
->i_mount
== I_MOUNT
&& rip
->i_num
!= ROOT_INODE
) {
177 rip
= advance(&dir_ip
, "..");
178 if (rip
== NIL_INODE
)
180 printf("%s, %d\n", __FILE__
, __LINE__
);
183 put_inode(rip
); /* advance() increased the counter */
186 dup_inode(rip
); /* inode will be returned with put_inode */
188 /* Looking for the starting directory?
189 * Note: this happens after EENTERMOUNT or ELEAVEMOUNT
190 * without more path component */
195 if (string
== (char *) 0) string
= lstring
;
197 /* Scan the path component by component. */
199 /* Extract one component. */
200 fs_m_out
.RES_OFFSET
= path_processed
; /* For ENOENT */
201 if ( (new_name
= get_name(path
, string
)) == (char*) 0) {
202 put_inode(rip
); /* bad path in user space */
203 printf("%s, %d\n", __FILE__
, __LINE__
);
206 if (*new_name
== '\0' && (action
& PATH_PENULTIMATE
)) {
207 if ( (rip
->i_mode
& I_TYPE
) == I_DIRECTORY
) {
208 return(rip
); /* normal exit */
210 /* last file of path prefix is not a directory */
213 printf("%s, %d\n", __FILE__
, __LINE__
);
218 /* There is more path. Keep parsing. */
220 rip
= advance(&dir_ip
, string
);
222 /* Mount point encountered? */
223 if (rip
== NIL_INODE
&& (err_code
== EENTERMOUNT
||
224 err_code
== ELEAVEMOUNT
)) {
229 if (rip
== NIL_INODE
) {
230 if (*new_name
== '\0' && (action
& PATH_NONSYMBOLIC
) != 0)
232 printf("%s, %d\n", __FILE__
, __LINE__
);
235 else if (err_code
== ENOENT
)
241 printf("%s, %d\n", __FILE__
, __LINE__
);
246 /* The call to advance() succeeded. Fetch next component. */
247 if (S_ISLNK(rip
->i_mode
)) {
248 if (*new_name
!= '\0' || (action
& PATH_OPAQUE
) == 0) {
250 if (*new_name
!= '\0') new_name
--;
252 /* Extract path name from the symlink file */
253 if (ltraverse(rip
, user_path
, new_name
,
254 sizeof(user_path
)) != OK
) {
257 printf("%s, %d\n", __FILE__
, __LINE__
);
261 /* Symloop limit reached? */
262 if (++symloop
> SYMLOOP
) {
265 printf("%s, %d\n", __FILE__
, __LINE__
);
269 /* Start over counting */
272 /* Check whether new path is relative or absolute */
273 if (user_path
[0] == '/') {
277 fs_m_out
.RES_OFFSET
= path_processed
;
278 fs_m_out
.RES_SYMLOOP
= symloop
;
281 /* Path is relative */
289 else if (*new_name
!= '\0') {
295 /* Either last name reached or symbolic link is opaque */
296 if ((action
& PATH_NONSYMBOLIC
) != 0) {
298 printf("%s, %d\n", __FILE__
, __LINE__
);
307 /*===========================================================================*
309 *===========================================================================*/
310 PRIVATE
int ltraverse(rip
, path
, suffix
, pathlen
)
311 register struct inode
*rip
; /* symbolic link */
312 char *path
; /* path containing link */
313 char *suffix
; /* suffix following link within path */
316 /* Traverse a symbolic link. Copy the link text from the inode and insert
317 * the text into the path. Return error code or report success. Base
318 * directory has to be determined according to the first character of the
322 block_t b
; /* block containing link text */
323 struct buf
*bp
; /* buffer containing link text */
324 size_t sl
; /* length of link */
325 size_t tl
; /* length of suffix */
326 char *sp
; /* start of link text */
331 if ((b
= read_map(rip
, (off_t
) 0)) != NO_BLOCK
) {
332 bp
= get_block(rip
->i_dev
, b
, NORMAL
);
336 /* Insert symbolic text into path name. */
338 if (sl
> 0 && sl
+ tl
<= PATH_MAX
-1) {
340 panic(__FILE__
,"path too small for symlink", sl
+tl
);
341 memmove(path
+sl
, suffix
, tl
);
342 memmove(path
, sp
, sl
);
345 /* Copy back to VFS layer THIS SHOULD BE IN parse_path.
346 * sys_datacopy() error, if any, gets returned as r later.
348 r
= sys_datacopy(SELF_E
, (vir_bytes
) path
, FS_PROC_NR
,
349 (vir_bytes
) vfs_slink_storage
, (phys_bytes
) sl
+tl
+1);
351 dup_inode(bip = path[0] == '/' ? chroot_dir : ldip);
354 printf("mfs:%s:%d: sys_datacopy failed: %d\n",
355 __FILE__
, __LINE__
, r
);
357 } else panic(__FILE__
,"didn't copy symlink", sl
+tl
);
363 put_block(bp
, DIRECTORY_BLOCK
);
371 /*===========================================================================*
373 *===========================================================================*/
374 PUBLIC
struct inode
*advance(pdirp
, string
)
375 struct inode
**pdirp
; /* inode for directory to be searched */
376 char string
[NAME_MAX
]; /* component name to look for */
378 /* Given a directory and a component of a path, look up the component in
379 * the directory, find the inode, open it, and return a pointer to its inode
380 * slot. If it can't be done, return NIL_INODE.
383 register struct inode
*rip
, *dirp
;
384 register struct super_block
*sp
;
391 /* If 'string' is empty, yield same inode straight away. */
392 if (string
[0] == '\0') { return(get_inode(dirp
->i_dev
, (int) dirp
->i_num
)); }
394 /* Check for NIL_INODE. */
395 if (dirp
== NIL_INODE
) { return(NIL_INODE
); }
397 /* If 'string' is not present in the directory, signal error. */
398 if ( (r
= search_dir(dirp
, string
, &numb
, LOOK_UP
)) != OK
) {
403 /* Don't go beyond the current root directory, unless the string is dot2.
404 * Note: it has to be checked only if this FS process owns the chroot
405 * directory of the process */
406 if (chroot_dir
!= NIL_INODE
) {
407 if (dirp
== chroot_dir
&& strcmp(string
, "..") == 0 && string
!= dot2
)
408 return(get_inode(dirp
->i_dev
, (int) dirp
->i_num
));
411 /* The component has been found in the directory. Get inode. */
412 if ( (rip
= get_inode(dirp
->i_dev
, (int) numb
)) == NIL_INODE
) {
416 /* The following test is for "mountpoint/.." where mountpoint is a
417 * mountpoint. ".." will refer to the root of the mounted filesystem,
418 * but has to become a reference to the parent of the 'mountpoint'
421 * This case is recognized by the looked up name pointing to a
422 * root inode, and the directory in which it is held being a
423 * root inode, _and_ the name[1] being '.'. (This is a test for '..'
426 if (rip
->i_num
== ROOT_INODE
) {
427 if (dirp
->i_num
== ROOT_INODE
) {
428 if (string
[1] == '.') {
430 if (!sp
->s_is_root
) {
431 /*printf("FSadvance: ELEAVEMOUNT callnr: %d, cp: %d, restp: %s\n",
432 call_nr, path_processed, user_path + path_processed);*/
434 /* Climbing up mountpoint */
435 err_code
= ELEAVEMOUNT
;
436 /* This will be the FS process endoint */
437 fs_m_out
.m_source
= rip
->i_dev
;
438 fs_m_out
.RES_OFFSET
= path_processed
;
439 fs_m_out
.RES_SYMLOOP
= symloop
;
447 if (rip
== NIL_INODE
) return(NIL_INODE
);
449 /* See if the inode is mounted on. If so, switch to root directory of the
450 * mounted file system. The super_block provides the linkage between the
451 * inode mounted on and the root directory of the mounted file system.
453 if (rip
!= NIL_INODE
&& rip
->i_mount
== I_MOUNT
) {
454 /*printf("FSadvance: EENTERMOUNT callnr: %d, cp: %d, vmnti: %d, restp: %s\n",
455 call_nr, path_processed, rip->i_vmnt_ind, user_path + path_processed);*/
457 /* Mountpoint encountered, report it */
458 err_code
= EENTERMOUNT
;
459 fs_m_out
.RES_INODE_NR
= rip
->i_num
;
460 fs_m_out
.RES_OFFSET
= path_processed
;
461 fs_m_out
.RES_SYMLOOP
= symloop
;
465 return(rip
); /* return pointer to inode's component */
469 /*===========================================================================*
471 *===========================================================================*/
472 PRIVATE
char *get_name(old_name
, string
)
473 char *old_name
; /* path name to parse */
474 char string
[NAME_MAX
]; /* component extracted from 'old_name' */
476 /* Given a pointer to a path name in fs space, 'old_name', copy the next
477 * component to 'string' and pad with zeros. A pointer to that part of
478 * the name as yet unparsed is returned. Roughly speaking,
479 * 'get_name' = 'old_name' - 'string'.
481 * This routine follows the standard convention that /usr/ast, /usr//ast,
482 * //usr///ast and /usr/ast/ are all equivalent.
486 register char *np
, *rnp
;
488 np
= string
; /* 'np' points to current position */
489 rnp
= old_name
; /* 'rnp' points to unparsed string */
490 while ( (c
= *rnp
) == '/') {
491 rnp
++; /* skip leading slashes */
492 path_processed
++; /* count characters */
495 /* Copy the unparsed path, 'old_name', to the array, 'string'. */
496 while ( rnp
< &old_name
[PATH_MAX
] && c
!= '/' && c
!= '\0') {
497 if (np
< &string
[NAME_MAX
]) *np
++ = c
;
498 c
= *++rnp
; /* advance to next character */
499 path_processed
++; /* count characters */
502 /* To make /usr/ast/ equivalent to /usr/ast, skip trailing slashes. */
503 while (c
== '/' && rnp
< &old_name
[PATH_MAX
]) {
505 path_processed
++; /* count characters */
508 if (np
< &string
[NAME_MAX
]) *np
= '\0'; /* Terminate string */
510 if (rnp
>= &old_name
[PATH_MAX
]) {
511 err_code
= ENAMETOOLONG
;
517 /*===========================================================================*
519 *===========================================================================*/
520 PUBLIC
int search_dir(ldir_ptr
, string
, numb
, flag
)
521 register struct inode
*ldir_ptr
; /* ptr to inode for dir to search */
522 char string
[NAME_MAX
]; /* component to search for */
523 ino_t
*numb
; /* pointer to inode number */
524 int flag
; /* LOOK_UP, ENTER, DELETE or IS_EMPTY */
526 /* This function searches the directory whose inode is pointed to by 'ldip':
527 * if (flag == ENTER) enter 'string' in the directory with inode # '*numb';
528 * if (flag == DELETE) delete 'string' from the directory;
529 * if (flag == LOOK_UP) search for 'string' and return inode # in 'numb';
530 * if (flag == IS_EMPTY) return OK if only . and .. in dir else ENOTEMPTY;
532 * if 'string' is dot1 or dot2, no access permissions are checked.
535 register struct direct
*dp
= NULL
;
536 register struct buf
*bp
= NULL
;
537 int i
, r
, e_hit
, t
, match
;
540 unsigned new_slots
, old_slots
;
542 struct super_block
*sp
;
545 /* If 'ldir_ptr' is not a pointer to a dir inode, error. */
546 if ( (ldir_ptr
->i_mode
& I_TYPE
) != I_DIRECTORY
) {
552 if (flag
!= IS_EMPTY
) {
553 bits
= (flag
== LOOK_UP
? X_BIT
: W_BIT
| X_BIT
);
555 if (string
== dot1
|| string
== dot2
) {
556 if (flag
!= LOOK_UP
) r
= read_only(ldir_ptr
);
557 /* only a writable device is required. */
559 else r
= forbidden(ldir_ptr
, bits
); /* check access permissions */
561 if (r
!= OK
) return(r
);
563 /* Step through the directory one block at a time. */
564 old_slots
= (unsigned) (ldir_ptr
->i_size
/DIR_ENTRY_SIZE
);
567 match
= 0; /* set when a string match occurs */
569 for (pos
= 0; pos
< ldir_ptr
->i_size
; pos
+= ldir_ptr
->i_sp
->s_block_size
) {
570 b
= read_map(ldir_ptr
, pos
); /* get block number */
572 /* Since directories don't have holes, 'b' cannot be NO_BLOCK. */
573 bp
= get_block(ldir_ptr
->i_dev
, b
, NORMAL
); /* get a dir block */
576 panic(__FILE__
,"get_block returned NO_BLOCK", NO_NUM
);
578 /* Search a directory block. */
579 for (dp
= &bp
->b_dir
[0];
580 dp
< &bp
->b_dir
[NR_DIR_ENTRIES(ldir_ptr
->i_sp
->s_block_size
)];
582 if (++new_slots
> old_slots
) { /* not found, but room left */
583 if (flag
== ENTER
) e_hit
= TRUE
;
587 /* Match occurs if string found. */
588 if (flag
!= ENTER
&& dp
->d_ino
!= 0) {
589 if (flag
== IS_EMPTY
) {
590 /* If this test succeeds, dir is not empty. */
591 if (strcmp(dp
->d_name
, "." ) != 0 &&
592 strcmp(dp
->d_name
, "..") != 0) match
= 1;
594 if (strncmp(dp
->d_name
, string
, NAME_MAX
) == 0){
601 /* LOOK_UP or DELETE found what it wanted. */
603 if (flag
== IS_EMPTY
) r
= ENOTEMPTY
;
604 else if (flag
== DELETE
) {
605 /* Save d_ino for recovery. */
606 t
= NAME_MAX
- sizeof(ino_t
);
607 *((ino_t
*) &dp
->d_name
[t
]) = dp
->d_ino
;
608 dp
->d_ino
= 0; /* erase entry */
610 ldir_ptr
->i_update
|= CTIME
| MTIME
;
611 ldir_ptr
->i_dirt
= DIRTY
;
613 sp
= ldir_ptr
->i_sp
; /* 'flag' is LOOK_UP */
614 *numb
= conv4(sp
->s_native
, (int) dp
->d_ino
);
616 put_block(bp
, DIRECTORY_BLOCK
);
620 /* Check for free slot for the benefit of ENTER. */
621 if (flag
== ENTER
&& dp
->d_ino
== 0) {
622 e_hit
= TRUE
; /* we found a free slot */
627 /* The whole block has been searched or ENTER has a free slot. */
628 if (e_hit
) break; /* e_hit set if ENTER can be performed now */
629 put_block(bp
, DIRECTORY_BLOCK
); /* otherwise, continue searching dir */
632 /* The whole directory has now been searched. */
634 return(flag
== IS_EMPTY
? OK
: ENOENT
);
637 /* This call is for ENTER. If no free slot has been found so far, try to
640 if (e_hit
== FALSE
) { /* directory is full and no room left in last block */
641 new_slots
++; /* increase directory size by 1 entry */
642 if (new_slots
== 0) return(EFBIG
); /* dir size limited by slot count */
643 if ( (bp
= new_block(ldir_ptr
, ldir_ptr
->i_size
)) == NIL_BUF
)
649 /* 'bp' now points to a directory block with space. 'dp' points to slot. */
650 (void) memset(dp
->d_name
, 0, (size_t) NAME_MAX
); /* clear entry */
651 for (i
= 0; i
< NAME_MAX
&& string
[i
]; i
++) dp
->d_name
[i
] = string
[i
];
653 dp
->d_ino
= conv4(sp
->s_native
, (int) *numb
);
655 put_block(bp
, DIRECTORY_BLOCK
);
656 ldir_ptr
->i_update
|= CTIME
| MTIME
; /* mark mtime for update later */
657 ldir_ptr
->i_dirt
= DIRTY
;
658 if (new_slots
> old_slots
) {
659 ldir_ptr
->i_size
= (off_t
) new_slots
* DIR_ENTRY_SIZE
;
660 /* Send the change to disk if the directory is extended. */
661 if (extended
) rw_inode(ldir_ptr
, WRITING
);
667 /*===========================================================================*
669 *===========================================================================*/
670 PUBLIC
struct inode
*eat_path(path
)
671 char *path
; /* the path name to be parsed */
673 /* Parse the path 'path' and put its inode in the inode table. If not possible,
674 * return NIL_INODE as function value and an error code in 'err_code'.
677 return parse_path(path
, (char *) 0, EAT_PATH
);
680 /*===========================================================================*
682 *===========================================================================*/
683 PUBLIC
struct inode
*last_dir(path
, string
)
684 char *path
; /* the path name to be parsed */
685 char string
[NAME_MAX
]; /* the final component is returned here */
687 /* Given a path, 'path', located in the fs address space, parse it as
688 * far as the last directory, fetch the inode for the last directory into
689 * the inode table, and return a pointer to the inode. In
690 * addition, return the final component of the path in 'string'.
691 * If the last directory can't be opened, return NIL_INODE and
692 * the reason for failure in 'err_code'.
695 return parse_path(path
, string
, LAST_DIR
);