1 /* VTreeFS - path.c - by Alen Stojanov and David van Moolenbroek */ 2 3 #include "inc.h" 4 5 /*===========================================================================* 6 * access_as_dir * 7 *===========================================================================*/ 8 static int access_as_dir(struct inode *node, vfs_ucred_t *ucred) 9 { 10 /* Check whether the given inode may be accessed as directory. 11 * Return OK or an appropriate error code. 12 */ 13 mode_t mask; 14 int i; 15 16 /* The inode must be a directory to begin with. */ 17 if (!S_ISDIR(node->i_stat.mode)) return ENOTDIR; 18 19 /* The caller must have search access to the directory. 20 * Root always does. 21 */ 22 if (ucred->vu_uid == SUPER_USER) return OK; 23 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; 28 29 for (i = 0; i < ucred->vu_ngroups; i++) { 30 if (ucred->vu_sgroups[i] == node->i_stat.gid) { 31 mask = S_IXGRP; 32 33 break; 34 } 35 } 36 } 37 38 return (node->i_stat.mode & mask) ? OK : EACCES; 39 } 40 41 /*===========================================================================* 42 * next_name * 43 *===========================================================================*/ 44 static int next_name(char **ptr, char **start, char name[PNAME_MAX+1]) 45 { 46 /* Get the next path component from a path. 47 */ 48 char *p; 49 int i; 50 51 for (p = *ptr; *p == '/'; p++); 52 53 *start = p; 54 55 if (*p) { 56 for (i = 0; *p && *p != '/' && i <= PNAME_MAX; p++, i++) 57 name[i] = *p; 58 59 if (i > PNAME_MAX) 60 return ENAMETOOLONG; 61 62 name[i] = 0; 63 } else { 64 strcpy(name, "."); 65 } 66 67 *ptr = p; 68 return OK; 69 } 70 71 /*===========================================================================* 72 * go_up * 73 *===========================================================================*/ 74 static int go_up(struct inode *node, struct inode **parent) 75 { 76 /* Given a directory inode, progress into the parent directory. 77 */ 78 79 *parent = get_parent_inode(node); 80 81 /* Trapped in a deleted directory? Should not be possible. */ 82 if (*parent == NULL) 83 return ENOENT; 84 85 ref_inode(*parent); 86 87 return OK; 88 } 89 90 /*===========================================================================* 91 * go_down * 92 *===========================================================================*/ 93 static int go_down(struct inode *parent, char *name, struct inode **child) 94 { 95 /* Given a directory inode and a name, progress into a directory entry. 96 */ 97 int r; 98 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; 104 } 105 106 if ((*child = get_inode_by_name(parent, name)) == NULL) 107 return ENOENT; 108 109 ref_inode(*child); 110 111 return OK; 112 } 113 114 /*===========================================================================* 115 * resolve_link * 116 *===========================================================================*/ 117 static int resolve_link(struct inode *node, char pptr[PATH_MAX], char *tail) 118 { 119 /* Given a symbolic link, resolve and return the contents of the link. 120 */ 121 char path[PATH_MAX]; 122 size_t len; 123 int r; 124 125 assert(vtreefs_hooks->rdlink_hook != NULL); 126 assert(!is_inode_deleted(node)); 127 128 r = vtreefs_hooks->rdlink_hook(node, path, sizeof(path), 129 get_inode_cbdata(node)); 130 if (r != OK) return r; 131 132 len = strlen(path); 133 assert(len > 0 && len < sizeof(path)); 134 135 if (len + strlen(tail) >= sizeof(path)) 136 return ENAMETOOLONG; 137 138 strlcat(path, tail, sizeof(path)); 139 140 strlcpy(pptr, path, PATH_MAX); 141 142 return OK; 143 } 144 145 /*===========================================================================* 146 * fs_lookup * 147 *===========================================================================*/ 148 int fs_lookup(void) 149 { 150 /* Resolve a path string to an inode. 151 */ 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; 159 160 dir_ino_nr = fs_m_in.m_vfs_fs_lookup.dir_ino; 161 root_ino_nr = fs_m_in.m_vfs_fs_lookup.root_ino; 162 len = fs_m_in.m_vfs_fs_lookup.path_len; 163 164 /* Fetch the path name. */ 165 if (len < 1 || len > PATH_MAX) 166 return EINVAL; 167 168 r = sys_safecopyfrom(fs_m_in.m_source, 169 fs_m_in.m_vfs_fs_lookup.grant_path, 0, (vir_bytes) path, 170 (phys_bytes) len); 171 if (r != OK) return r; 172 173 if (path[len-1] != 0) return EINVAL; 174 175 /* Fetch the caller's credentials. */ 176 if (fs_m_in.m_vfs_fs_lookup.flags & PATH_GET_UCRED) { 177 assert(fs_m_in.m_vfs_fs_lookup.ucred_size == sizeof(ucred)); 178 179 r = sys_safecopyfrom(fs_m_in.m_source, 180 fs_m_in.m_vfs_fs_lookup.grant_ucred, 0, 181 (vir_bytes) &ucred, fs_m_in.m_vfs_fs_lookup.ucred_size); 182 183 if (r != OK) 184 return r; 185 } 186 else { 187 ucred.vu_uid = fs_m_in.m_vfs_fs_lookup.uid; 188 ucred.vu_gid = fs_m_in.m_vfs_fs_lookup.gid; 189 ucred.vu_ngroups = 0; 190 } 191 192 /* Start the actual lookup. */ 193 if ((cur_ino = get_inode(dir_ino_nr)) == NULL) 194 return EINVAL; 195 196 /* Chroot'ed environment? */ 197 if (root_ino_nr > 0) 198 root_ino = find_inode(root_ino_nr); 199 else 200 root_ino = NULL; 201 202 symloop = 0; 203 204 for (ptr = last = path; ptr[0] != 0; ) { 205 /* There is more path to process. That means that the current 206 * file is now being accessed as a directory. Check type and 207 * permissions. 208 */ 209 if ((r = access_as_dir(cur_ino, &ucred)) != OK) 210 break; 211 212 /* Get the next path component. The result is a non-empty 213 * string. 214 */ 215 if ((r = next_name(&ptr, &last, name)) != OK) 216 break; 217 218 if (!strcmp(name, ".") || 219 (cur_ino == root_ino && !strcmp(name, ".."))) 220 continue; 221 222 if (!strcmp(name, "..")) { 223 if (cur_ino == get_root_inode()) 224 r = ELEAVEMOUNT; 225 else 226 r = go_up(cur_ino, &next_ino); 227 } else { 228 r = go_down(cur_ino, name, &next_ino); 229 230 /* Perform symlink resolution if we have to. */ 231 if (r == OK && S_ISLNK(next_ino->i_stat.mode) && 232 (ptr[0] != '\0' || 233 !(fs_m_in.m_vfs_fs_lookup.flags & PATH_RET_SYMLINK))) { 234 235 if (++symloop == _POSIX_SYMLOOP_MAX) { 236 put_inode(next_ino); 237 238 r = ELOOP; 239 240 break; 241 } 242 243 /* Resolve the symlink, and append the 244 * remaining unresolved part of the path. 245 */ 246 r = resolve_link(next_ino, path, ptr); 247 248 put_inode(next_ino); 249 250 if (r != OK) 251 break; 252 253 /* If the symlink is absolute, return it to 254 * VFS. 255 */ 256 if (path[0] == '/') { 257 r = ESYMLINK; 258 last = path; 259 260 break; 261 } 262 263 ptr = path; 264 continue; 265 } 266 } 267 268 if (r != OK) 269 break; 270 271 /* We have found a new file. Continue from this file. */ 272 assert(next_ino != NULL); 273 274 put_inode(cur_ino); 275 276 cur_ino = next_ino; 277 } 278 279 /* If an error occurred, close the file and return error information. 280 */ 281 if (r != OK) { 282 put_inode(cur_ino); 283 284 /* We'd need support for this here. */ 285 assert(r != EENTERMOUNT); 286 287 /* Copy back the path if we resolved at least one symlink. */ 288 if (symloop > 0 && (r == ELEAVEMOUNT || r == ESYMLINK)) { 289 r2 = sys_safecopyto(fs_m_in.m_source, 290 fs_m_in.m_vfs_fs_lookup.grant_path, 0, 291 (vir_bytes) path, strlen(path) + 1); 292 293 if (r2 != OK) 294 r = r2; 295 } 296 297 if (r == ELEAVEMOUNT || r == ESYMLINK) { 298 fs_m_out.m_fs_vfs_lookup.offset = (int) (last - path); 299 fs_m_out.m_fs_vfs_lookup.symloop = symloop; 300 } 301 302 return r; 303 } 304 305 /* On success, leave the resulting file open and return its details. */ 306 fs_m_out.m_fs_vfs_lookup.inode = get_inode_number(cur_ino); 307 fs_m_out.m_fs_vfs_lookup.mode = cur_ino->i_stat.mode; 308 fs_m_out.m_fs_vfs_lookup.file_size = cur_ino->i_stat.size; 309 fs_m_out.m_fs_vfs_lookup.uid = cur_ino->i_stat.uid; 310 fs_m_out.m_fs_vfs_lookup.gid = cur_ino->i_stat.gid; 311 fs_m_out.m_fs_vfs_lookup.device = cur_ino->i_stat.dev; 312 313 return OK; 314 } 315