1 /* VTreeFS - path.c - by Alen Stojanov and David van Moolenbroek */
5 /*===========================================================================*
7 *===========================================================================*/
8 static int access_as_dir(struct inode
*node
, vfs_ucred_t
*ucred
)
10 /* Check whether the given inode may be accessed as directory.
11 * Return OK or an appropriate error code.
16 /* The inode must be a directory to begin with. */
17 if (!S_ISDIR(node
->i_stat
.mode
)) return ENOTDIR
;
19 /* The caller must have search access to the directory.
22 if (ucred
->vu_uid
== SUPER_USER
) return OK
;
24 if (ucred
->vu_uid
== node
->i_stat
.uid
) mask
= S_IXUSR
;
25 else if (ucred
->vu_gid
== node
->i_stat
.gid
) mask
= S_IXGRP
;
29 for (i
= 0; i
< ucred
->vu_ngroups
; i
++) {
30 if (ucred
->vu_sgroups
[i
] == node
->i_stat
.gid
) {
38 return (node
->i_stat
.mode
& mask
) ? OK
: EACCES
;
41 /*===========================================================================*
43 *===========================================================================*/
44 static int next_name(char **ptr
, char **start
, char name
[PNAME_MAX
+1])
46 /* Get the next path component from a path.
51 for (p
= *ptr
; *p
== '/'; p
++);
56 for (i
= 0; *p
&& *p
!= '/' && i
<= PNAME_MAX
; p
++, i
++)
71 /*===========================================================================*
73 *===========================================================================*/
74 static int go_up(struct inode
*node
, struct inode
**parent
)
76 /* Given a directory inode, progress into the parent directory.
79 *parent
= get_parent_inode(node
);
81 /* Trapped in a deleted directory? Should not be possible. */
90 /*===========================================================================*
92 *===========================================================================*/
93 static int go_down(struct inode
*parent
, char *name
, struct inode
**child
)
95 /* Given a directory inode and a name, progress into a directory entry.
99 /* Call the lookup hook, if present, before doing the actual lookup. */
100 if (!is_inode_deleted(parent
) && vtreefs_hooks
->lookup_hook
!= NULL
) {
101 r
= vtreefs_hooks
->lookup_hook(parent
, name
,
102 get_inode_cbdata(parent
));
103 if (r
!= OK
) return r
;
106 if ((*child
= get_inode_by_name(parent
, name
)) == NULL
)
114 /*===========================================================================*
116 *===========================================================================*/
117 static int resolve_link(struct inode
*node
, char pptr
[PATH_MAX
], char *tail
)
119 /* Given a symbolic link, resolve and return the contents of the link.
125 assert(vtreefs_hooks
->rdlink_hook
!= NULL
);
126 assert(!is_inode_deleted(node
));
128 r
= vtreefs_hooks
->rdlink_hook(node
, path
, sizeof(path
),
129 get_inode_cbdata(node
));
130 if (r
!= OK
) return r
;
133 assert(len
> 0 && len
< sizeof(path
));
135 if (len
+ strlen(tail
) >= sizeof(path
))
138 strlcat(path
, tail
, sizeof(path
));
140 strlcpy(pptr
, path
, PATH_MAX
);
145 /*===========================================================================*
147 *===========================================================================*/
150 /* Resolve a path string to an inode.
152 ino_t dir_ino_nr
, root_ino_nr
;
153 struct inode
*cur_ino
, *next_ino
, *root_ino
;
154 char path
[PATH_MAX
], name
[PNAME_MAX
+1];
160 dir_ino_nr
= fs_m_in
.REQ_DIR_INO
;
161 root_ino_nr
= fs_m_in
.REQ_ROOT_INO
;
162 len
= fs_m_in
.REQ_PATH_LEN
;
164 /* Fetch the path name. */
165 if (len
< 1 || len
> PATH_MAX
)
168 r
= sys_safecopyfrom(fs_m_in
.m_source
, fs_m_in
.REQ_GRANT
, 0,
169 (vir_bytes
) path
, (phys_bytes
) len
);
170 if (r
!= OK
) return r
;
172 if (path
[len
-1] != 0) return EINVAL
;
174 /* Fetch the caller's credentials. */
175 if (fs_m_in
.REQ_FLAGS
& PATH_GET_UCRED
) {
176 assert(fs_m_in
.REQ_UCRED_SIZE
== sizeof(ucred
));
178 r
= sys_safecopyfrom(fs_m_in
.m_source
, fs_m_in
.REQ_GRANT2
, 0,
179 (vir_bytes
) &ucred
, fs_m_in
.REQ_UCRED_SIZE
);
185 ucred
.vu_uid
= fs_m_in
.REQ_UID
;
186 ucred
.vu_gid
= fs_m_in
.REQ_GID
;
187 ucred
.vu_ngroups
= 0;
190 /* Start the actual lookup. */
191 if ((cur_ino
= get_inode(dir_ino_nr
)) == NULL
)
194 /* Chroot'ed environment? */
196 root_ino
= find_inode(root_ino_nr
);
202 for (ptr
= last
= path
; ptr
[0] != 0; ) {
203 /* There is more path to process. That means that the current
204 * file is now being accessed as a directory. Check type and
207 if ((r
= access_as_dir(cur_ino
, &ucred
)) != OK
)
210 /* Get the next path component. The result is a non-empty
213 if ((r
= next_name(&ptr
, &last
, name
)) != OK
)
216 if (!strcmp(name
, ".") ||
217 (cur_ino
== root_ino
&& !strcmp(name
, "..")))
220 if (!strcmp(name
, "..")) {
221 if (cur_ino
== get_root_inode())
224 r
= go_up(cur_ino
, &next_ino
);
226 r
= go_down(cur_ino
, name
, &next_ino
);
228 /* Perform symlink resolution if we have to. */
229 if (r
== OK
&& S_ISLNK(next_ino
->i_stat
.mode
) &&
231 !(fs_m_in
.REQ_FLAGS
& PATH_RET_SYMLINK
))) {
233 if (++symloop
== SYMLOOP_MAX
) {
241 /* Resolve the symlink, and append the
242 * remaining unresolved part of the path.
244 r
= resolve_link(next_ino
, path
, ptr
);
251 /* If the symlink is absolute, return it to
254 if (path
[0] == '/') {
269 /* We have found a new file. Continue from this file. */
270 assert(next_ino
!= NULL
);
277 /* If an error occurred, close the file and return error information.
282 /* We'd need support for this here. */
283 assert(r
!= EENTERMOUNT
);
285 /* Copy back the path if we resolved at least one symlink. */
286 if (symloop
> 0 && (r
== ELEAVEMOUNT
|| r
== ESYMLINK
)) {
287 r2
= sys_safecopyto(fs_m_in
.m_source
,
288 fs_m_in
.REQ_GRANT
, 0, (vir_bytes
) path
,
295 if (r
== ELEAVEMOUNT
|| r
== ESYMLINK
) {
296 fs_m_out
.RES_OFFSET
= (int) (last
- path
);
297 fs_m_out
.RES_SYMLOOP
= symloop
;
303 /* On success, leave the resulting file open and return its details. */
304 fs_m_out
.RES_INODE_NR
= get_inode_number(cur_ino
);
305 fs_m_out
.RES_MODE
= cur_ino
->i_stat
.mode
;
306 fs_m_out
.RES_FILE_SIZE_HI
= 0;
307 fs_m_out
.RES_FILE_SIZE_LO
= cur_ino
->i_stat
.size
;
308 fs_m_out
.RES_UID
= cur_ino
->i_stat
.uid
;
309 fs_m_out
.RES_GID
= cur_ino
->i_stat
.gid
;
310 fs_m_out
.RES_DEV
= cur_ino
->i_stat
.dev
;