5 * Check whether the given node may be accessed as directory.
6 * Return OK or an appropriate error code.
9 access_as_dir(struct fsdriver_node
* __restrict node
,
10 vfs_ucred_t
* __restrict ucred
)
15 /* The file must be a directory to begin with. */
16 if (!S_ISDIR(node
->fn_mode
)) return ENOTDIR
;
18 /* The root user may access anything at all. */
19 if (ucred
->vu_uid
== ROOT_UID
) return OK
;
21 /* Otherwise, the caller must have search access to the directory. */
22 if (ucred
->vu_uid
== node
->fn_uid
) mask
= S_IXUSR
;
23 else if (ucred
->vu_gid
== node
->fn_gid
) mask
= S_IXGRP
;
27 for (i
= 0; i
< ucred
->vu_ngroups
; i
++) {
28 if (ucred
->vu_sgroups
[i
] == node
->fn_gid
) {
36 return (node
->fn_mode
& mask
) ? OK
: EACCES
;
40 * Get the next path component from a path. Return the start and end of the
41 * component into the path, and store its name in a null-terminated buffer.
44 next_name(char ** ptr
, char ** start
, char * __restrict name
, size_t namesize
)
49 /* Skip one or more path separator characters; they have no effect. */
50 for (p
= *ptr
; *p
== '/'; p
++);
56 * Copy as much of the name as possible, up to the next path
57 * separator. Return an error if the name does not fit.
59 for (i
= 0; *p
&& *p
!= '/' && i
< namesize
; p
++, i
++)
67 /* An empty path component implies the current directory. */
68 strlcpy(name
, ".", namesize
);
71 * Return a pointer to the first character not part of this component.
72 * This would typically be either the path separator or a null.
79 * Given a symbolic link, resolve and return the contents of the link, followed
80 * by the remaining part of the path that has not yet been resolved (the tail).
81 * Note that the tail points into the given destination buffer.
84 resolve_link(const struct fsdriver
* __restrict fdp
, ino_t ino_nr
, char * pptr
,
85 size_t size
, char * tail
)
87 struct fsdriver_data data
;
93 data
.size
= sizeof(path
) - 1;
96 * Let the file system the symbolic link. Note that the resulting path
97 * is not null-terminated.
99 if ((r
= fdp
->fdr_rdlink(ino_nr
, &data
, data
.size
)) < 0)
102 /* Append the remaining part of the original path to be resolved. */
103 if (r
+ strlen(tail
) >= sizeof(path
))
106 strlcpy(&path
[r
], tail
, sizeof(path
) - r
);
108 /* Copy back the result to the original buffer. */
109 strlcpy(pptr
, path
, size
);
115 * Process a LOOKUP request from VFS.
118 fsdriver_lookup(const struct fsdriver
* __restrict fdp
,
119 const message
* __restrict m_in
, message
* __restrict m_out
)
121 ino_t dir_ino_nr
, root_ino_nr
;
122 struct fsdriver_node cur_node
, next_node
;
123 char path
[PATH_MAX
], name
[NAME_MAX
+1];
125 cp_grant_id_t path_grant
;
128 size_t path_len
, path_size
;
129 int r
, r2
, going_up
, is_mountpt
, symloop
;
131 if (fdp
->fdr_lookup
== NULL
)
134 dir_ino_nr
= m_in
->m_vfs_fs_lookup
.dir_ino
;
135 root_ino_nr
= m_in
->m_vfs_fs_lookup
.root_ino
;
136 path_grant
= m_in
->m_vfs_fs_lookup
.grant_path
;
137 path_size
= m_in
->m_vfs_fs_lookup
.path_size
;
138 path_len
= m_in
->m_vfs_fs_lookup
.path_len
;
139 flags
= m_in
->m_vfs_fs_lookup
.flags
;
141 /* Fetch the path name. */
142 if ((r
= fsdriver_getname(m_in
->m_source
, path_grant
, path_len
, path
,
143 sizeof(path
), FALSE
/*not_empty*/)) != OK
)
146 /* Fetch the caller's credentials. */
147 if (flags
& PATH_GET_UCRED
) {
148 if (m_in
->m_vfs_fs_lookup
.ucred_size
!= sizeof(ucred
)) {
149 printf("fsdriver: bad credential structure\n");
154 if ((r
= sys_safecopyfrom(m_in
->m_source
,
155 m_in
->m_vfs_fs_lookup
.grant_ucred
, 0, (vir_bytes
)&ucred
,
156 (phys_bytes
)m_in
->m_vfs_fs_lookup
.ucred_size
)) != OK
)
159 ucred
.vu_uid
= m_in
->m_vfs_fs_lookup
.uid
;
160 ucred
.vu_gid
= m_in
->m_vfs_fs_lookup
.gid
;
161 ucred
.vu_ngroups
= 0;
164 /* Start the actual lookup by referencing the starting inode. */
165 strlcpy(name
, ".", sizeof(name
)); /* allow a non-const argument */
167 r
= fdp
->fdr_lookup(dir_ino_nr
, name
, &cur_node
, &is_mountpt
);
173 /* Whenever we leave this loop, 'cur_node' holds a referenced inode. */
174 for (ptr
= last
= path
; *ptr
!= 0; ) {
176 * Get the next path component. The result is a non-empty
179 if ((r
= next_name(&ptr
, &last
, name
, sizeof(name
))) != OK
)
184 * If we start off from a mount point, the next path
185 * component *must* cause us to go up. Anything else
186 * is a protocol violation.
188 if (strcmp(name
, "..")) {
194 * There is more path to process. That means that the
195 * current file is now being accessed as a directory.
196 * Check type and permissions.
198 if ((r
= access_as_dir(&cur_node
, &ucred
)) != OK
)
202 /* A single-dot component resolves to the current directory. */
203 if (!strcmp(name
, "."))
206 /* A dot-dot component resolves to the parent directory. */
207 going_up
= !strcmp(name
, "..");
211 * The parent of the process's root directory is the
212 * same root directory. All processes have a root
213 * directory, so this check also covers the case of
214 * going up from the global system root directory.
216 if (cur_node
.fn_ino_nr
== root_ino_nr
)
220 * Going up from the file system's root directory means
221 * crossing mount points. As indicated, the root file
222 * system is already covered by the check above.
224 if (cur_node
.fn_ino_nr
== fsdriver_root
) {
233 * Descend into a child node or go up to a parent node, by
234 * asking the actual file system to perform a one-step
235 * resolution. The result, if successful, is an open
236 * (referenced) inode.
238 if ((r
= fdp
->fdr_lookup(cur_node
.fn_ino_nr
, name
, &next_node
,
242 /* Sanity check: a parent node must always be a directory. */
243 if (going_up
&& !S_ISDIR(next_node
.fn_mode
))
244 panic("fsdriver: ascending into nondirectory");
247 * Perform symlink resolution, unless the symlink is the last
248 * path component and VFS is asking us not to resolve it.
250 if (S_ISLNK(next_node
.fn_mode
) &&
251 (*ptr
|| !(flags
& PATH_RET_SYMLINK
))) {
253 * Resolve the symlink, and append the remaining
254 * unresolved part of the path.
256 if (++symloop
< _POSIX_SYMLOOP_MAX
)
257 r
= resolve_link(fdp
, next_node
.fn_ino_nr
,
258 path
, sizeof(path
), ptr
);
262 if (fdp
->fdr_putnode
!= NULL
)
263 fdp
->fdr_putnode(next_node
.fn_ino_nr
, 1);
270 /* If the symlink is absolute, return it to VFS. */
271 if (path
[0] == '/') {
279 /* We have found a new node. Continue from this node. */
280 if (fdp
->fdr_putnode
!= NULL
)
281 fdp
->fdr_putnode(cur_node
.fn_ino_nr
, 1);
283 cur_node
= next_node
;
286 * If the new node is a mount point, yield to another file
295 /* For special redirection errors, we need to return extra details. */
296 if (r
== EENTERMOUNT
|| r
== ELEAVEMOUNT
|| r
== ESYMLINK
) {
297 /* Copy back the path if we resolved at least one symlink. */
299 if ((path_len
= strlen(path
) + 1) > path_size
)
302 r2
= sys_safecopyto(m_in
->m_source
, path_grant
, 0,
303 (vir_bytes
)path
, (phys_bytes
)path_len
);
308 m_out
->m_fs_vfs_lookup
.offset
= (int)(ptr
- path
);
309 m_out
->m_fs_vfs_lookup
.symloop
= symloop
;
311 if (r
== EENTERMOUNT
)
312 m_out
->m_fs_vfs_lookup
.inode
=
319 * On success, leave the resulting file open and return its details.
320 * If an error occurred, close the file and return error information.
323 m_out
->m_fs_vfs_lookup
.inode
= cur_node
.fn_ino_nr
;
324 m_out
->m_fs_vfs_lookup
.mode
= cur_node
.fn_mode
;
325 m_out
->m_fs_vfs_lookup
.file_size
= cur_node
.fn_size
;
326 m_out
->m_fs_vfs_lookup
.uid
= cur_node
.fn_uid
;
327 m_out
->m_fs_vfs_lookup
.gid
= cur_node
.fn_gid
;
328 m_out
->m_fs_vfs_lookup
.device
= cur_node
.fn_dev
;
329 } else if (fdp
->fdr_putnode
!= NULL
)
330 fdp
->fdr_putnode(cur_node
.fn_ino_nr
, 1);