int searchdir(const char *name, int flags) { static char root_name[] = "/"; struct file *file; char *path, *inode_name, *next_inode_name; struct inode *tmp, *inode = NULL; int symlink_count = MAX_SYMLINK_CNT; dprintf("searchdir: %s root: %p cwd: %p\n", name, this_fs->root, this_fs->cwd); if (!(file = alloc_file())) goto err_no_close; file->fs = this_fs; /* if we have ->searchdir method, call it */ if (file->fs->fs_ops->searchdir) { file->fs->fs_ops->searchdir(name, flags, file); if (file->inode) return file_to_handle(file); else goto err; } /* else, try the generic-path-lookup method */ /* Copy the path */ path = strdup(name); if (!path) { dprintf("searchdir: Couldn't copy path\n"); goto err_path; } /* Work with the current directory, by default */ inode = get_inode(this_fs->cwd); if (!inode) { dprintf("searchdir: Couldn't use current directory\n"); goto err_curdir; } for (inode_name = path; inode_name; inode_name = next_inode_name) { /* Root directory? */ if (inode_name[0] == '/') { next_inode_name = inode_name + 1; inode_name = root_name; } else { /* Find the next inode name */ next_inode_name = strchr(inode_name + 1, '/'); if (next_inode_name) { /* Terminate the current inode name and point to next */ *next_inode_name++ = '\0'; } } if (next_inode_name) { /* Advance beyond redundant slashes */ while (*next_inode_name == '/') next_inode_name++; /* Check if we're at the end */ if (*next_inode_name == '\0') next_inode_name = NULL; } dprintf("searchdir: inode_name: %s\n", inode_name); if (next_inode_name) dprintf("searchdir: Remaining: %s\n", next_inode_name); /* Root directory? */ if (inode_name[0] == '/') { /* Release any chain that's already been established */ put_inode(inode); inode = get_inode(this_fs->root); continue; } /* Current directory? */ if (!strncmp(inode_name, ".", sizeof ".")) continue; /* Parent directory? */ if (!strncmp(inode_name, "..", sizeof "..")) { /* If there is no parent, just ignore it */ if (!inode->parent) continue; /* Add a reference to the parent so we can release the child */ tmp = get_inode(inode->parent); /* Releasing the child will drop the parent back down to 1 */ put_inode(inode); inode = tmp; continue; } /* Anything else */ tmp = inode; inode = this_fs->fs_ops->iget(inode_name, inode); if (!inode) { /* Failure. Release the chain */ put_inode(tmp); break; } /* Sanity-check */ if (inode->parent && inode->parent != tmp) { dprintf("searchdir: iget returned a different parent\n"); put_inode(inode); inode = NULL; put_inode(tmp); break; } inode->parent = tmp; inode->name = strdup(inode_name); dprintf("searchdir: path component: %s\n", inode->name); /* Symlink handling */ if (inode->mode == DT_LNK) { char *new_path; int new_len, copied; /* target path + NUL */ new_len = inode->size + 1; if (next_inode_name) { /* target path + slash + remaining + NUL */ new_len += strlen(next_inode_name) + 1; } if (!this_fs->fs_ops->readlink || /* limit checks */ --symlink_count == 0 || new_len > MAX_SYMLINK_BUF) goto err_new_len; new_path = malloc(new_len); if (!new_path) goto err_new_path; copied = this_fs->fs_ops->readlink(inode, new_path); if (copied <= 0) goto err_copied; new_path[copied] = '\0'; dprintf("searchdir: Symlink: %s\n", new_path); if (next_inode_name) { new_path[copied] = '/'; strcpy(new_path + copied + 1, next_inode_name); dprintf("searchdir: New path: %s\n", new_path); } free(path); path = next_inode_name = new_path; /* Add a reference to the parent so we can release the child */ tmp = get_inode(inode->parent); /* Releasing the child will drop the parent back down to 1 */ put_inode(inode); inode = tmp; continue; err_copied: free(new_path); err_new_path: err_new_len: put_inode(inode); inode = NULL; break; } /* If there's more to process, this should be a directory */ if (next_inode_name && inode->mode != DT_DIR) { dprintf("searchdir: Expected a directory\n"); put_inode(inode); inode = NULL; break; } } err_curdir: free(path); err_path: if (!inode) { dprintf("searchdir: Not found\n"); goto err; } file->inode = inode; file->offset = 0; return file_to_handle(file); err: dprintf("serachdir: error seraching file %s\n", name); _close_file(file); err_no_close: return -1; }
int searchdir(const char *name) { struct inode *inode = NULL; struct inode *parent = NULL; struct file *file; char *pathbuf = NULL; char *part, *p, echar; int symlink_count = MAX_SYMLINK_CNT; dprintf("searchdir: %s root: %p cwd: %p\n", name, this_fs->root, this_fs->cwd); if (!(file = alloc_file())) goto err_no_close; file->fs = this_fs; /* if we have ->searchdir method, call it */ if (file->fs->fs_ops->searchdir) { file->fs->fs_ops->searchdir(name, file); if (file->inode) return file_to_handle(file); else goto err; } /* else, try the generic-path-lookup method */ parent = get_inode(this_fs->cwd); p = pathbuf = strdup(name); if (!pathbuf) goto err; do { got_link: if (*p == '/') { put_inode(parent); parent = get_inode(this_fs->root); } do { inode = get_inode(parent); while (*p == '/') p++; if (!*p) break; part = p; while ((echar = *p) && echar != '/') p++; *p++ = '\0'; if (part[0] == '.' && part[1] == '.' && part[2] == '\0') { if (inode->parent) { put_inode(parent); parent = get_inode(inode->parent); put_inode(inode); inode = NULL; if (!echar) { /* Terminal double dots */ inode = parent; parent = inode->parent ? get_inode(inode->parent) : NULL; } } } else if (part[0] != '.' || part[1] != '\0') { inode = this_fs->fs_ops->iget(part, parent); if (!inode) goto err; if (inode->mode == DT_LNK) { char *linkbuf, *q; int name_len = echar ? strlen(p) : 0; int total_len = inode->size + name_len + 2; int link_len; if (!this_fs->fs_ops->readlink || --symlink_count == 0 || /* limit check */ total_len > MAX_SYMLINK_BUF) goto err; linkbuf = malloc(total_len); if (!linkbuf) goto err; link_len = this_fs->fs_ops->readlink(inode, linkbuf); if (link_len <= 0) { free(linkbuf); goto err; } q = linkbuf + link_len; if (echar) { if (link_len > 0 && q[-1] != '/') *q++ = '/'; memcpy(q, p, name_len+1); } else { *q = '\0'; } free(pathbuf); p = pathbuf = linkbuf; put_inode(inode); inode = NULL; goto got_link; } inode->name = strdup(part); dprintf("path component: %s\n", inode->name); inode->parent = parent; parent = NULL; if (!echar) break; if (inode->mode != DT_DIR) goto err; parent = inode; inode = NULL; } } while (echar); } while (0); free(pathbuf); pathbuf = NULL; put_inode(parent); parent = NULL; if (!inode) goto err; file->inode = inode; file->offset = 0; return file_to_handle(file); err: put_inode(inode); put_inode(parent); if (pathbuf) free(pathbuf); _close_file(file); err_no_close: return -1; }