/* This is the main workhorse for the virtual filesystem. Given a start point and * a path string, it resolves the path into a directory entry. This is simply a * matter of looping through the path (seperated at '/', of course), and reading * the successive directory entries and inodes (see vfs_dirent_lookup and * vfs_dirent_readinode). The one thing it needs to pay attention to is the case * of traversing backwards up through a mount point. */ struct dirent *fs_do_path_resolve(struct inode *start, const char *path, int sym_start_level, int *result) { vfs_inode_get(start); ASSERT(start); if(!*path) path = "."; if(result) *result = 0; struct inode *node = start; struct dirent *dir = 0; struct inode *nextnode = 0; while(node && *path) { if(dir) { vfs_dirent_release(dir); dir = 0; } char *delim = strchr(path, '/'); if(delim != path) { nextnode = 0; const char *name = path; size_t namelen = delim ? (size_t)(delim - name) : strlen(name); dir = fs_dirent_lookup(node, name, namelen); if(!dir) { if(result) *result = -ENOENT; vfs_icache_put(node); return 0; } if(delim) { nextnode = fs_dirent_readinode(dir, true); if(namelen == 2 && !strncmp("..", name, 2) && node->id == node->filesystem->root_inode_id) { // Traverse back up through a mount. vfs_inode_get(nextnode->filesystem->point); struct inode *tmp = nextnode; nextnode = nextnode->filesystem->point; vfs_icache_put(tmp); } else if(nextnode) { if((*result = fs_resolve_iter_symlink(&dir, &nextnode, sym_start_level))) { vfs_icache_put(node); return 0; } nextnode = fs_resolve_mount(nextnode); if(!nextnode) { vfs_icache_put(node); return 0; } } } vfs_icache_put(node); node = nextnode; } path = delim + 1; } if(nextnode) vfs_icache_put(nextnode); return dir; }
int sys_openpty(int *master, int *slave, char *slavename, const struct termios *term, const struct winsize *win) { int num = atomic_fetch_add(&__pty_next_num, 1); char mname[32]; char sname[32]; snprintf(mname, 32, "/dev/ptym%d", num); snprintf(sname, 32, "/dev/ptys%d", num); sys_mknod(mname, S_IFCHR | 0666, GETDEV(pty_major, num)); sys_mknod(sname, S_IFCHR | 0666, GETDEV(pty_major, num)); int mfd = sys_open(mname, O_RDWR, 0); int sfd = sys_open(sname, O_RDWR, 0); if(mfd < 0 || sfd < 0) { sys_unlink(mname); sys_unlink(sname); return -ENOENT; } struct file *mf = file_get(mfd); struct file *sf = file_get(sfd); vfs_inode_get(mf->inode); vfs_inode_get(sf->inode); pty_create(mf->inode); sf->inode->devdata = mf->inode->devdata; struct pty *pty = mf->inode->devdata; assert(pty); pty->master = mf->inode; pty->slave = sf->inode; pty->num = num; if(term) memcpy(&pty->term, term, sizeof(*term)); if(win) memcpy(&pty->size, win, sizeof(*win)); file_put(mf); file_put(sf); if(slavename) strncpy(slavename, sname, 32); if(master) *master = mfd; if(slave) *slave = sfd; return 0; }
void vfs_inode_mount(struct inode *node, struct filesystem *fs) { assert(!node->mount); node->mount = fs; vfs_inode_get(node); fs->point = node; }
int vfs_inode_chdir(struct inode *node) { if(!S_ISDIR(node->mode)) return -ENOTDIR; struct inode *old = current_process->cwd; current_process->cwd = node; vfs_inode_get(node); vfs_icache_put(old); return 0; }
int vfs_inode_chroot(struct inode *node) { if(!S_ISDIR(node->mode)) return -ENOTDIR; if(current_process->effective_uid) return -EPERM; struct inode *old = current_process->root; current_process->root = node; vfs_inode_get(node); vfs_icache_put(old); return 0; }
/* This function returns the directory entry associated with the name 'name' under * the inode 'node'. It must be careful to lookup the entry in the cache first. */ struct dirent *fs_dirent_lookup(struct inode *node, const char *name, size_t namelen) { if(!vfs_inode_check_permissions(node, MAY_READ, 0)) return 0; if(!S_ISDIR(node->mode)) return 0; if(node == current_process->root && !strncmp(name, "..", 2) && namelen == 2) return fs_dirent_lookup(node, ".", 1); mutex_acquire(dirent_cache_lock); rwlock_acquire(&node->lock, RWL_WRITER); struct dirent *dir = vfs_inode_get_dirent(node, name, namelen); if(!dir) { dir = vfs_dirent_create(node); dir->count = 1; strncpy(dir->name, name, namelen); dir->namelen = namelen; int r = fs_callback_inode_lookup(node, name, namelen, dir); if(r) { dir->count = 0; vfs_dirent_destroy(dir); rwlock_release(&node->lock, RWL_WRITER); mutex_release(dirent_cache_lock); return 0; } vfs_inode_get(node); vfs_inode_add_dirent(node, dir); } else { if(atomic_fetch_add(&dir->count, 1) == 0) { fs_dirent_remove_lru(dir); vfs_inode_get(node); } } rwlock_release(&node->lock, RWL_WRITER); mutex_release(dirent_cache_lock); return dir; }