retire BIOS_SEG and umap_bios
[minix3.git] / lib / libvtreefs / path.c
blobef8f3a2723c908f747d53e911ba17a3288b7ae4a
1 /* VTreeFS - path.c - by Alen Stojanov and David van Moolenbroek */
3 #include "inc.h"
5 /*===========================================================================*
6 * access_as_dir *
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.
13 mode_t mask;
14 int i;
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.
20 * Root always does.
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;
26 else {
27 mask = S_IXOTH;
29 for (i = 0; i < ucred->vu_ngroups; i++) {
30 if (ucred->vu_sgroups[i] == node->i_stat.gid) {
31 mask = S_IXGRP;
33 break;
38 return (node->i_stat.mode & mask) ? OK : EACCES;
41 /*===========================================================================*
42 * next_name *
43 *===========================================================================*/
44 static int next_name(char **ptr, char **start, char name[PNAME_MAX+1])
46 /* Get the next path component from a path.
48 char *p;
49 int i;
51 for (p = *ptr; *p == '/'; p++);
53 *start = p;
55 if (*p) {
56 for (i = 0; *p && *p != '/' && i <= PNAME_MAX; p++, i++)
57 name[i] = *p;
59 if (i > PNAME_MAX)
60 return ENAMETOOLONG;
62 name[i] = 0;
63 } else {
64 strcpy(name, ".");
67 *ptr = p;
68 return OK;
71 /*===========================================================================*
72 * go_up *
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. */
82 if (*parent == NULL)
83 return ENOENT;
85 ref_inode(*parent);
87 return OK;
90 /*===========================================================================*
91 * go_down *
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.
97 int r;
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)
107 return ENOENT;
109 ref_inode(*child);
111 return OK;
114 /*===========================================================================*
115 * resolve_link *
116 *===========================================================================*/
117 static int resolve_link(struct inode *node, char *pptr, char *tail)
119 /* Given a symbolic link, resolve and return the contents of the link.
121 char path[PATH_MAX];
122 size_t len;
123 int r;
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;
132 len = strlen(path);
133 assert(len > 0 && len < sizeof(path));
135 if (len + strlen(tail) >= sizeof(path))
136 return ENAMETOOLONG;
138 strcat(path, tail);
140 strcpy(pptr, path);
142 return OK;
145 /*===========================================================================*
146 * fs_lookup *
147 *===========================================================================*/
148 int fs_lookup(void)
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];
155 char *ptr, *last;
156 vfs_ucred_t ucred;
157 size_t len;
158 int r, r2, symloop;
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)
166 return EINVAL;
168 r = sys_safecopyfrom(fs_m_in.m_source, fs_m_in.REQ_GRANT, 0,
169 (vir_bytes) path, (phys_bytes) len, D);
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, D);
181 if (r != OK)
182 return r;
184 else {
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)
192 return EINVAL;
194 /* Chroot'ed environment? */
195 if (root_ino_nr > 0)
196 root_ino = find_inode(root_ino_nr);
197 else
198 root_ino = NULL;
200 symloop = 0;
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
205 * permissions.
207 if ((r = access_as_dir(cur_ino, &ucred)) != OK)
208 break;
210 /* Get the next path component. The result is a non-empty
211 * string.
213 if ((r = next_name(&ptr, &last, name)) != OK)
214 break;
216 if (!strcmp(name, ".") ||
217 (cur_ino == root_ino && !strcmp(name, "..")))
218 continue;
220 if (!strcmp(name, "..")) {
221 if (cur_ino == get_root_inode())
222 r = ELEAVEMOUNT;
223 else
224 r = go_up(cur_ino, &next_ino);
225 } else {
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) &&
230 (ptr[0] != '\0' ||
231 !(fs_m_in.REQ_FLAGS & PATH_RET_SYMLINK))) {
233 if (++symloop == SYMLOOP_MAX) {
234 put_inode(next_ino);
236 r = ELOOP;
238 break;
241 /* Resolve the symlink, and append the
242 * remaining unresolved part of the path.
244 r = resolve_link(next_ino, path, ptr);
246 put_inode(next_ino);
248 if (r != OK)
249 break;
251 /* If the symlink is absolute, return it to
252 * VFS.
254 if (path[0] == '/') {
255 r = ESYMLINK;
256 last = path;
258 break;
261 ptr = path;
262 continue;
266 if (r != OK)
267 break;
269 /* We have found a new file. Continue from this file. */
270 assert(next_ino != NULL);
272 put_inode(cur_ino);
274 cur_ino = next_ino;
277 /* If an error occurred, close the file and return error information.
279 if (r != OK) {
280 put_inode(cur_ino);
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,
289 strlen(path) + 1, D);
291 if (r2 != OK)
292 r = r2;
295 if (r == ELEAVEMOUNT || r == ESYMLINK) {
296 fs_m_out.RES_OFFSET = (int) (last - path);
297 fs_m_out.RES_SYMLOOP = symloop;
300 return r;
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;
312 return OK;