1 2 #include "fsdriver.h" 3 4 /* 5 * Check whether the given node may be accessed as directory. 6 * Return OK or an appropriate error code. 7 */ 8 static int 9 access_as_dir(struct fsdriver_node * __restrict node, 10 vfs_ucred_t * __restrict ucred) 11 { 12 mode_t mask; 13 int i; 14 15 /* The file must be a directory to begin with. */ 16 if (!S_ISDIR(node->fn_mode)) return ENOTDIR; 17 18 /* The root user may access anything at all. */ 19 if (ucred->vu_uid == ROOT_UID) return OK; 20 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; 24 else { 25 mask = S_IXOTH; 26 27 for (i = 0; i < ucred->vu_ngroups; i++) { 28 if (ucred->vu_sgroups[i] == node->fn_gid) { 29 mask = S_IXGRP; 30 31 break; 32 } 33 } 34 } 35 36 return (node->fn_mode & mask) ? OK : EACCES; 37 } 38 39 /* 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. 42 */ 43 static int 44 next_name(char ** ptr, char ** start, char * __restrict name, size_t namesize) 45 { 46 char *p; 47 unsigned int i; 48 49 /* Skip one or more path separator characters; they have no effect. */ 50 for (p = *ptr; *p == '/'; p++); 51 52 *start = p; 53 54 if (*p) { 55 /* 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. 58 */ 59 for (i = 0; *p && *p != '/' && i < namesize; p++, i++) 60 name[i] = *p; 61 62 if (i >= namesize) 63 return ENAMETOOLONG; 64 65 name[i] = 0; 66 } else 67 /* An empty path component implies the current directory. */ 68 strlcpy(name, ".", namesize); 69 70 /* 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. 73 */ 74 *ptr = p; 75 return OK; 76 } 77 78 /* 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. 82 */ 83 static int 84 resolve_link(const struct fsdriver * __restrict fdp, ino_t ino_nr, char * pptr, 85 size_t size, char * tail) 86 { 87 struct fsdriver_data data; 88 char path[PATH_MAX]; 89 ssize_t r; 90 91 data.endpt = SELF; 92 data.ptr = path; 93 data.size = sizeof(path) - 1; 94 95 /* 96 * Let the file system the symbolic link. Note that the resulting path 97 * is not null-terminated. 98 */ 99 if ((r = fdp->fdr_rdlink(ino_nr, &data, data.size)) < 0) 100 return r; 101 102 /* Append the remaining part of the original path to be resolved. */ 103 if (r + strlen(tail) >= sizeof(path)) 104 return ENAMETOOLONG; 105 106 strlcpy(&path[r], tail, sizeof(path) - r); 107 108 /* Copy back the result to the original buffer. */ 109 strlcpy(pptr, path, size); 110 111 return OK; 112 } 113 114 /* 115 * Process a LOOKUP request from VFS. 116 */ 117 int 118 fsdriver_lookup(const struct fsdriver * __restrict fdp, 119 const message * __restrict m_in, message * __restrict m_out) 120 { 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]; 124 char *ptr, *last; 125 cp_grant_id_t path_grant; 126 vfs_ucred_t ucred; 127 unsigned int flags; 128 size_t path_len, path_size; 129 int r, r2, going_up, is_mountpt, symloop; 130 131 if (fdp->fdr_lookup == NULL) 132 return ENOSYS; 133 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; 140 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) 144 return r; 145 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"); 150 151 return EINVAL; 152 } 153 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) 157 return r; 158 } else { 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; 162 } 163 164 /* Start the actual lookup by referencing the starting inode. */ 165 strlcpy(name, ".", sizeof(name)); /* allow a non-const argument */ 166 167 r = fdp->fdr_lookup(dir_ino_nr, name, &cur_node, &is_mountpt); 168 if (r != OK) 169 return r; 170 171 symloop = 0; 172 173 /* Whenever we leave this loop, 'cur_node' holds a referenced inode. */ 174 for (ptr = last = path; *ptr != 0; ) { 175 /* 176 * Get the next path component. The result is a non-empty 177 * string. 178 */ 179 if ((r = next_name(&ptr, &last, name, sizeof(name))) != OK) 180 break; 181 182 if (is_mountpt) { 183 /* 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. 187 */ 188 if (strcmp(name, "..")) { 189 r = EINVAL; 190 break; 191 } 192 } else { 193 /* 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. 197 */ 198 if ((r = access_as_dir(&cur_node, &ucred)) != OK) 199 break; 200 } 201 202 /* A single-dot component resolves to the current directory. */ 203 if (!strcmp(name, ".")) 204 continue; 205 206 /* A dot-dot component resolves to the parent directory. */ 207 going_up = !strcmp(name, ".."); 208 209 if (going_up) { 210 /* 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. 215 */ 216 if (cur_node.fn_ino_nr == root_ino_nr) 217 continue; 218 219 /* 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. 223 */ 224 if (cur_node.fn_ino_nr == fsdriver_root) { 225 ptr = last; 226 227 r = ELEAVEMOUNT; 228 break; 229 } 230 } 231 232 /* 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. 237 */ 238 if ((r = fdp->fdr_lookup(cur_node.fn_ino_nr, name, &next_node, 239 &is_mountpt)) != OK) 240 break; 241 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"); 245 246 /* 247 * Perform symlink resolution, unless the symlink is the last 248 * path component and VFS is asking us not to resolve it. 249 */ 250 if (S_ISLNK(next_node.fn_mode) && 251 (*ptr || !(flags & PATH_RET_SYMLINK))) { 252 /* 253 * Resolve the symlink, and append the remaining 254 * unresolved part of the path. 255 */ 256 if (++symloop < _POSIX_SYMLOOP_MAX) 257 r = resolve_link(fdp, next_node.fn_ino_nr, 258 path, sizeof(path), ptr); 259 else 260 r = ELOOP; 261 262 fdp->fdr_putnode(next_node.fn_ino_nr, 1); 263 264 if (r != OK) 265 break; 266 267 ptr = path; 268 269 /* If the symlink is absolute, return it to VFS. */ 270 if (path[0] == '/') { 271 r = ESYMLINK; 272 break; 273 } 274 275 continue; 276 } 277 278 /* We have found a new node. Continue from this node. */ 279 fdp->fdr_putnode(cur_node.fn_ino_nr, 1); 280 281 cur_node = next_node; 282 283 /* 284 * If the new node is a mount point, yield to another file 285 * system. 286 */ 287 if (is_mountpt) { 288 r = EENTERMOUNT; 289 break; 290 } 291 } 292 293 /* For special redirection errors, we need to return extra details. */ 294 if (r == EENTERMOUNT || r == ELEAVEMOUNT || r == ESYMLINK) { 295 /* Copy back the path if we resolved at least one symlink. */ 296 if (symloop > 0) { 297 if ((path_len = strlen(path) + 1) > path_size) 298 return ENAMETOOLONG; 299 300 r2 = sys_safecopyto(m_in->m_source, path_grant, 0, 301 (vir_bytes)path, (phys_bytes)path_len); 302 } else 303 r2 = OK; 304 305 if (r2 == OK) { 306 m_out->m_fs_vfs_lookup.offset = (int)(ptr - path); 307 m_out->m_fs_vfs_lookup.symloop = symloop; 308 309 if (r == EENTERMOUNT) 310 m_out->m_fs_vfs_lookup.inode = 311 cur_node.fn_ino_nr; 312 } else 313 r = r2; 314 } 315 316 /* 317 * On success, leave the resulting file open and return its details. 318 * If an error occurred, close the file and return error information. 319 */ 320 if (r == OK) { 321 m_out->m_fs_vfs_lookup.inode = cur_node.fn_ino_nr; 322 m_out->m_fs_vfs_lookup.mode = cur_node.fn_mode; 323 m_out->m_fs_vfs_lookup.file_size = cur_node.fn_size; 324 m_out->m_fs_vfs_lookup.uid = cur_node.fn_uid; 325 m_out->m_fs_vfs_lookup.gid = cur_node.fn_gid; 326 m_out->m_fs_vfs_lookup.device = cur_node.fn_dev; 327 } else 328 fdp->fdr_putnode(cur_node.fn_ino_nr, 1); 329 330 return r; 331 } 332