1 /* This file provides path-to-inode lookup functionality. 2 * 3 * The entry points into this file are: 4 * do_lookup perform the LOOKUP file system call 5 * 6 * Created: 7 * April 2009 (D.C. van Moolenbroek) 8 */ 9 10 #include "inc.h" 11 12 static int get_mask(vfs_ucred_t *ucred); 13 14 static int access_as_dir(struct inode *ino, struct sffs_attr *attr, 15 int uid, int mask); 16 17 static int next_name(char **ptr, char **start, char name[NAME_MAX+1]); 18 19 static int go_up(char path[PATH_MAX], struct inode *ino, 20 struct inode **res_ino, struct sffs_attr *attr); 21 22 static int go_down(char path[PATH_MAX], struct inode *ino, char *name, 23 struct inode **res_ino, struct sffs_attr *attr); 24 25 /*===========================================================================* 26 * get_mask * 27 *===========================================================================*/ 28 static int get_mask( 29 vfs_ucred_t *ucred /* credentials of the caller */ 30 ) 31 { 32 /* Given the caller's credentials, precompute a search access mask to test 33 * against directory modes. 34 */ 35 int i; 36 37 if (ucred->vu_uid == sffs_params->p_uid) return S_IXUSR; 38 39 if (ucred->vu_gid == sffs_params->p_gid) return S_IXGRP; 40 41 for (i = 0; i < ucred->vu_ngroups; i++) 42 if (ucred->vu_sgroups[i] == sffs_params->p_gid) return S_IXGRP; 43 44 return S_IXOTH; 45 } 46 47 /*===========================================================================* 48 * access_as_dir * 49 *===========================================================================*/ 50 static int access_as_dir( 51 struct inode *ino, /* the inode to test */ 52 struct sffs_attr *attr, /* attributes of the inode */ 53 int uid, /* UID of the caller */ 54 int mask /* search access mask of the caller */ 55 ) 56 { 57 /* Check whether the given inode may be accessed as directory. 58 * Return OK or an appropriate error code. 59 */ 60 mode_t mode; 61 62 assert(attr->a_mask & SFFS_ATTR_MODE); 63 64 /* The inode must be a directory to begin with. */ 65 if (!IS_DIR(ino)) return ENOTDIR; 66 67 /* The caller must have search access to the directory. Root always does. */ 68 if (uid == 0) return OK; 69 70 mode = get_mode(ino, attr->a_mode); 71 72 return (mode & mask) ? OK : EACCES; 73 } 74 75 /*===========================================================================* 76 * next_name * 77 *===========================================================================*/ 78 static int next_name( 79 char **ptr, /* cursor pointer into path (in, out) */ 80 char **start, /* place to store start of name */ 81 char name[NAME_MAX+1] /* place to store name */ 82 ) 83 { 84 /* Get the next path component from a path. 85 */ 86 char *p; 87 int i; 88 89 for (p = *ptr; *p == '/'; p++); 90 91 *start = p; 92 93 if (*p) { 94 for (i = 0; *p && *p != '/' && i <= NAME_MAX; p++, i++) 95 name[i] = *p; 96 97 if (i > NAME_MAX) 98 return ENAMETOOLONG; 99 100 name[i] = 0; 101 } else { 102 strcpy(name, "."); 103 } 104 105 *ptr = p; 106 return OK; 107 } 108 109 /*===========================================================================* 110 * go_up * 111 *===========================================================================*/ 112 static int go_up( 113 char path[PATH_MAX], /* path to take the last part from */ 114 struct inode *ino, /* inode of the current directory */ 115 struct inode **res_ino, /* place to store resulting inode */ 116 struct sffs_attr *attr /* place to store inode attributes */ 117 ) 118 { 119 /* Given an inode, progress into the parent directory. 120 */ 121 struct inode *parent; 122 int r; 123 124 pop_path(path); 125 126 parent = ino->i_parent; 127 assert(parent != NULL); 128 129 if ((r = verify_path(path, parent, attr, NULL)) != OK) 130 return r; 131 132 get_inode(parent); 133 134 *res_ino = parent; 135 136 return r; 137 } 138 139 /*===========================================================================* 140 * go_down * 141 *===========================================================================*/ 142 static int go_down( 143 char path[PATH_MAX], /* path to add the name to */ 144 struct inode *parent, /* inode of the current directory */ 145 char *name, /* name of the directory entry */ 146 struct inode **res_ino, /* place to store resulting inode */ 147 struct sffs_attr *attr /* place to store inode attributes */ 148 ) 149 { 150 /* Given a directory inode and a name, progress into a directory entry. 151 */ 152 struct inode *ino; 153 int r, stale = 0; 154 155 if ((r = push_path(path, name)) != OK) 156 return r; 157 158 dprintf(("%s: go_down: name '%s', path now '%s'\n", sffs_name, name, path)); 159 160 ino = lookup_dentry(parent, name); 161 162 dprintf(("%s: lookup_dentry('%s') returned %p\n", sffs_name, name, ino)); 163 164 if (ino != NULL) 165 r = verify_path(path, ino, attr, &stale); 166 else 167 r = sffs_table->t_getattr(path, attr); 168 169 dprintf(("%s: path query returned %d\n", sffs_name, r)); 170 171 if (r != OK) { 172 if (ino != NULL) { 173 put_inode(ino); 174 175 ino = NULL; 176 } 177 178 if (!stale) 179 return r; 180 } 181 182 dprintf(("%s: name '%s'\n", sffs_name, name)); 183 184 if (ino == NULL) { 185 if ((ino = get_free_inode()) == NULL) 186 return ENFILE; 187 188 dprintf(("%s: inode %p ref %d\n", sffs_name, ino, ino->i_ref)); 189 190 ino->i_flags = MODE_TO_DIRFLAG(attr->a_mode); 191 192 add_dentry(parent, name, ino); 193 } 194 195 *res_ino = ino; 196 return OK; 197 } 198 199 /*===========================================================================* 200 * do_lookup * 201 *===========================================================================*/ 202 int do_lookup(void) 203 { 204 /* Resolve a path string to an inode. 205 */ 206 ino_t dir_ino_nr, root_ino_nr; 207 struct inode *cur_ino, *root_ino; 208 struct inode *next_ino = NULL; 209 struct sffs_attr attr; 210 char buf[PATH_MAX], path[PATH_MAX]; 211 char name[NAME_MAX+1]; 212 char *ptr, *last; 213 vfs_ucred_t ucred; 214 mode_t mask; 215 size_t len; 216 int r; 217 218 dir_ino_nr = m_in.m_vfs_fs_lookup.dir_ino; 219 root_ino_nr = m_in.m_vfs_fs_lookup.root_ino; 220 len = m_in.m_vfs_fs_lookup.path_len; 221 222 /* Fetch the path name. */ 223 if (len < 1 || len > PATH_MAX) 224 return EINVAL; 225 226 r = sys_safecopyfrom(m_in.m_source, m_in.m_vfs_fs_lookup.grant_path, 0, 227 (vir_bytes) buf, len); 228 229 if (r != OK) 230 return r; 231 232 if (buf[len-1] != 0) { 233 printf("%s: VFS did not zero-terminate path!\n", sffs_name); 234 235 return EINVAL; 236 } 237 238 /* Fetch the credentials, and generate a search access mask to test against 239 * directory modes. 240 */ 241 if (m_in.m_vfs_fs_lookup.flags & PATH_GET_UCRED) { 242 if (m_in.m_vfs_fs_lookup.ucred_size != sizeof(ucred)) { 243 printf("%s: bad credential structure size\n", sffs_name); 244 245 return EINVAL; 246 } 247 248 r = sys_safecopyfrom(m_in.m_source, m_in.m_vfs_fs_lookup.grant_ucred, 0, 249 (vir_bytes) &ucred, m_in.m_vfs_fs_lookup.ucred_size); 250 251 if (r != OK) 252 return r; 253 } 254 else { 255 ucred.vu_uid = m_in.m_vfs_fs_lookup.uid; 256 ucred.vu_gid = m_in.m_vfs_fs_lookup.gid; 257 ucred.vu_ngroups = 0; 258 } 259 260 mask = get_mask(&ucred); 261 262 /* Start the actual lookup. */ 263 dprintf(("%s: lookup: got query '%s'\n", sffs_name, buf)); 264 265 if ((cur_ino = find_inode(dir_ino_nr)) == NULL) 266 return EINVAL; 267 268 attr.a_mask = SFFS_ATTR_MODE | SFFS_ATTR_SIZE; 269 270 if ((r = verify_inode(cur_ino, path, &attr)) != OK) 271 return r; 272 273 get_inode(cur_ino); 274 275 if (root_ino_nr > 0) 276 root_ino = find_inode(root_ino_nr); 277 else 278 root_ino = NULL; 279 280 /* One possible optimization would be to check a path only right before the 281 * first ".." in a row, and at the very end (if still necessary). This would 282 * have consequences for inode validation, though. 283 */ 284 for (ptr = last = buf; *ptr != 0; ) { 285 if ((r = access_as_dir(cur_ino, &attr, ucred.vu_uid, mask)) != OK) 286 break; 287 288 if ((r = next_name(&ptr, &last, name)) != OK) 289 break; 290 291 dprintf(("%s: lookup: next name '%s'\n", sffs_name, name)); 292 293 if (!strcmp(name, ".") || 294 (cur_ino == root_ino && !strcmp(name, ".."))) 295 continue; 296 297 if (!strcmp(name, "..")) { 298 if (IS_ROOT(cur_ino)) 299 r = ELEAVEMOUNT; 300 else 301 r = go_up(path, cur_ino, &next_ino, &attr); 302 } else { 303 r = go_down(path, cur_ino, name, &next_ino, &attr); 304 } 305 306 if (r != OK) 307 break; 308 309 assert(next_ino != NULL); 310 311 put_inode(cur_ino); 312 313 cur_ino = next_ino; 314 } 315 316 dprintf(("%s: lookup: result %d\n", sffs_name, r)); 317 318 if (r != OK) { 319 put_inode(cur_ino); 320 321 /* We'd need support for these here. We don't have such support. */ 322 assert(r != EENTERMOUNT && r != ESYMLINK); 323 324 if (r == ELEAVEMOUNT) { 325 m_out.m_fs_vfs_lookup.offset = (last - buf); 326 m_out.m_fs_vfs_lookup.symloop = 0; 327 } 328 329 return r; 330 } 331 332 m_out.m_fs_vfs_lookup.inode = INODE_NR(cur_ino); 333 m_out.m_fs_vfs_lookup.mode = get_mode(cur_ino, attr.a_mode); 334 m_out.m_fs_vfs_lookup.file_size = attr.a_size; 335 m_out.m_fs_vfs_lookup.uid = sffs_params->p_uid; 336 m_out.m_fs_vfs_lookup.gid = sffs_params->p_gid; 337 m_out.m_fs_vfs_lookup.device = NO_DEV; 338 339 return OK; 340 } 341