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>
22 PUBLIC
char dot1
[2] = "."; /* used for search_dir to bypass the access */
23 PUBLIC
char dot2
[3] = ".."; /* permissions for . and .. */
25 FORWARD
_PROTOTYPE( char *get_name
, (char *old_name
, char string
[NAME_MAX
]) );
27 FORWARD
_PROTOTYPE( struct inode
*ltraverse
, (struct inode
*rip
,
28 char *path
, char *suffix
, struct inode
*ldip
) );
30 /*===========================================================================*
32 *===========================================================================*/
33 PUBLIC
struct inode
*parse_path(path
, string
, action
)
34 char *path
; /* the path name to be parsed */
35 char string
[NAME_MAX
]; /* the final component is returned here */
36 int action
; /* action on last part of path */
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.
46 struct inode
*rip
, *dir_ip
;
49 char lstring
[NAME_MAX
];
51 /* Is the path absolute or relative? Initialize 'rip' accordingly. */
52 rip
= (*path
== '/' ? fp
->fp_rootdir
: fp
->fp_workdir
);
54 /* If dir has been removed or path is empty, return ENOENT. */
55 if (rip
->i_nlinks
== 0 || *path
== '\0') {
60 dup_inode(rip
); /* inode will be returned with put_inode */
62 symloop
= 0; /* symbolic link traversal count */
63 if (string
== (char *) 0) string
= lstring
;
65 /* Scan the path component by component. */
67 /* Extract one component. */
68 if ( (new_name
= get_name(path
, string
)) == (char*) 0) {
69 put_inode(rip
); /* bad path in user space */
72 if (*new_name
== '\0' && (action
& PATH_PENULTIMATE
)) {
73 if ( (rip
->i_mode
& I_TYPE
) == I_DIRECTORY
) {
74 return(rip
); /* normal exit */
76 /* last file of path prefix is not a directory */
83 /* There is more path. Keep parsing. */
85 rip
= advance(&dir_ip
, string
);
87 if (rip
== NIL_INODE
) {
88 if (*new_name
== '\0' && (action
& PATH_NONSYMBOLIC
) != 0)
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
);
102 if (++symloop
> SYMLOOP
) {
107 if (rip
== NIL_INODE
) return(NIL_INODE
);
110 } else if (*new_name
!= '\0') {
116 /* Either last name reached or symbolic link is opaque */
117 if ((action
& PATH_NONSYMBOLIC
) != 0) {
127 /*===========================================================================*
129 *===========================================================================*/
130 PUBLIC
struct inode
*eat_path(path
)
131 char *path
; /* the path name to be parsed */
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'.
137 return parse_path(path
, (char *) 0, EAT_PATH
);
140 /*===========================================================================*
142 *===========================================================================*/
143 PUBLIC
struct inode
*last_dir(path
, string
)
144 char *path
; /* the path name to be parsed */
145 char string
[NAME_MAX
]; /* the final component is returned here */
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'.
155 return parse_path(path
, string
, LAST_DIR
);
158 /*===========================================================================*
160 *===========================================================================*/
161 PRIVATE
struct inode
*ltraverse(rip
, path
, suffix
, ldip
)
162 register struct inode
*rip
; /* symbolic link */
163 char *path
; /* path containing link */
164 char *suffix
; /* suffix following link within path */
165 register struct inode
*ldip
; /* directory containing link */
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.
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 */
183 if ((b
= read_map(rip
, (off_t
) 0)) != NO_BLOCK
) {
184 bp
= get_block(rip
->i_dev
, b
, NORMAL
);
188 /* Insert symbolic text into path name. */
190 if (sl
> 0 && sl
+ tl
<= PATH_MAX
-1) {
191 memmove(path
+sl
, suffix
, tl
);
192 memmove(path
, sp
, sl
);
194 dup_inode(bip
= path
[0] == '/' ? fp
->fp_rootdir
: ldip
);
198 put_block(bp
, DIRECTORY_BLOCK
);
200 if (bip
== NIL_INODE
)
207 /*===========================================================================*
209 *===========================================================================*/
210 PRIVATE
char *get_name(old_name
, string
)
211 char *old_name
; /* path name to parse */
212 char string
[NAME_MAX
]; /* component extracted from 'old_name' */
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'.
219 * This routine follows the standard convention that /usr/ast, /usr//ast,
220 * //usr///ast and /usr/ast/ are all equivalent.
224 register char *np
, *rnp
;
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 */
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 */
236 /* To make /usr/ast/ equivalent to /usr/ast, skip trailing slashes. */
237 while (c
== '/' && rnp
< &old_name
[PATH_MAX
]) c
= *++rnp
;
239 if (np
< &string
[NAME_MAX
]) *np
= '\0'; /* Terminate string */
241 if (rnp
>= &old_name
[PATH_MAX
]) {
242 err_code
= ENAMETOOLONG
;
248 /*===========================================================================*
250 *===========================================================================*/
251 PUBLIC
struct inode
*advance(pdirp
, string
)
252 struct inode
**pdirp
; /* inode for directory to be searched */
253 char string
[NAME_MAX
]; /* component name to look for */
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.
260 register struct inode
*rip
, *dirp
;
261 register struct super_block
*sp
;
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
)); }
271 /* Check for NIL_INODE. */
272 if (dirp
== NIL_INODE
) { return(NIL_INODE
); }
274 /* If 'string' is not present in the directory, signal error. */
275 if ( (r
= search_dir(dirp
, string
, &numb
, LOOK_UP
)) != OK
) {
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
));
284 /* The component has been found in the directory. Get inode. */
285 if ( (rip
= get_inode(dirp
->i_dev
, (int) numb
)) == NIL_INODE
) {
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'
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 '..'
299 if (rip
->i_num
== ROOT_INODE
)
300 if (dirp
->i_num
== ROOT_INODE
) {
301 if (string
[1] == '.') {
303 if (sp
->s_imount
!= sp
->s_isup
)
305 /* Release the root inode. Replace by the
306 * inode mounted on. Update parent.
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
);
317 if (rip
== NIL_INODE
) return(NIL_INODE
);
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.
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.
331 rip
= get_inode(sp
->s_dev
, ROOT_INODE
);
336 return(rip
); /* return pointer to inode's component */
339 /*===========================================================================*
341 *===========================================================================*/
342 PUBLIC
int search_dir(ldir_ptr
, string
, numb
, flag
)
343 register struct inode
*ldir_ptr
; /* ptr to inode for dir to search */
344 char string
[NAME_MAX
]; /* component to search for */
345 ino_t
*numb
; /* pointer to inode number */
346 int flag
; /* LOOK_UP, ENTER, DELETE or IS_EMPTY */
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;
354 * if 'string' is dot1 or dot2, no access permissions are checked.
357 register struct direct
*dp
= NULL
;
358 register struct buf
*bp
= NULL
;
359 int i
, r
, e_hit
, t
, match
;
362 unsigned new_slots
, old_slots
;
364 struct super_block
*sp
;
367 /* If 'ldir_ptr' is not a pointer to a dir inode, error. */
368 if ( (ldir_ptr
->i_mode
& I_TYPE
) != I_DIRECTORY
) {
374 if (flag
!= IS_EMPTY
) {
375 bits
= (flag
== LOOK_UP
? X_BIT
: W_BIT
| X_BIT
);
377 if (string
== dot1
|| string
== dot2
) {
378 if (flag
!= LOOK_UP
) r
= read_only(ldir_ptr
);
379 /* only a writable device is required. */
381 else r
= forbidden(ldir_ptr
, bits
); /* check access permissions */
383 if (r
!= OK
) return(r
);
385 /* Step through the directory one block at a time. */
386 old_slots
= (unsigned) (ldir_ptr
->i_size
/DIR_ENTRY_SIZE
);
389 match
= 0; /* set when a string match occurs */
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 */
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 */
398 panic(__FILE__
,"get_block returned NO_BLOCK", NO_NUM
);
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
)];
404 if (++new_slots
> old_slots
) { /* not found, but room left */
405 if (flag
== ENTER
) e_hit
= TRUE
;
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;
416 if (strncmp(dp
->d_name
, string
, NAME_MAX
) == 0) {
423 /* LOOK_UP or DELETE found what it wanted. */
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 */
432 ldir_ptr
->i_update
|= CTIME
| MTIME
;
433 ldir_ptr
->i_dirt
= DIRTY
;
435 sp
= ldir_ptr
->i_sp
; /* 'flag' is LOOK_UP */
436 *numb
= conv4(sp
->s_native
, (int) dp
->d_ino
);
438 put_block(bp
, DIRECTORY_BLOCK
);
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 */
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 */
454 /* The whole directory has now been searched. */
456 return(flag
== IS_EMPTY
? OK
: ENOENT
);
459 /* This call is for ENTER. If no free slot has been found so far, try to
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
)
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
];
475 dp
->d_ino
= conv4(sp
->s_native
, (int) *numb
);
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
);