/** * \brief File-system directory node * \author Norman Feske * \author Christian Helmuth * \author Sebastian Sumpf * \date 2013-11-11 */ /* * Copyright (C) 2013-2014 Genode Labs GmbH * * This file is part of the Genode OS framework, which is distributed * under the terms of the GNU General Public License version 2. */ #ifndef _DIRECTORY_H_ #define _DIRECTORY_H_ /* Genode include */ #include /* local includes */ #include "node.h" #include "util.h" #include "file.h" #include "symlink.h" namespace File_system { class Directory; } class File_system::Directory : public Node { private: enum { BUFFER_SIZE = 4096 }; typedef Genode::Path Path; int _fd; Path _path; Allocator &_alloc; unsigned long _inode(char const *path, bool create) { int ret; if (create) { mode_t ugo = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; ret = rump_sys_mkdir(path, ugo); if (ret == -1) throw No_space(); } struct stat s; ret = rump_sys_lstat(path, &s); if (ret == -1) throw Lookup_failed(); return s.st_ino; } int _open(char const *path) { struct stat s; int ret = rump_sys_lstat(path, &s); if (ret == -1 || !S_ISDIR(s.st_mode)) throw Lookup_failed(); int fd = rump_sys_open(path, O_RDONLY); if (fd == -1) throw Lookup_failed(); return fd; } static char *_buffer() { /* buffer for directory entries */ static char buf[BUFFER_SIZE]; return buf; } public: Directory(Allocator &alloc, char const *path, bool create) : Node(_inode(path, create)), _fd(_open(path)), _path(path, "./"), _alloc(alloc) { Node::name(basename(path)); } virtual ~Directory() { rump_sys_close(_fd); } File * file(char const *name, Mode mode, bool create) { return new (&_alloc) File(_fd, name, mode, create); } Symlink * symlink(char const *name, bool create) { return new (&_alloc) Symlink(_path.base(), name, create); } Directory * subdir(char const *path, bool create) { Path dir_path(path, _path.base()); Directory *dir = new (&_alloc) Directory(_alloc, dir_path.base(), create); return dir; } Node * node(char const *path) { Path node_path(path, _path.base()); struct stat s; int ret = rump_sys_lstat(node_path.base(), &s); if (ret == -1) throw Lookup_failed(); Node *node = 0; if (S_ISDIR(s.st_mode)) node = new (&_alloc) Directory(_alloc, node_path.base(), false); else if (S_ISREG(s.st_mode)) node = new (&_alloc) File(node_path.base(), STAT_ONLY); else if (S_ISLNK(s.st_mode)) node = new (&_alloc)Symlink(node_path.base()); else throw Lookup_failed(); return node; } size_t read(char *dst, size_t len, seek_off_t seek_offset) { if (len < sizeof(Directory_entry)) { PERR("read buffer too small for directory entry"); return 0; } if (seek_offset % sizeof(Directory_entry)) { PERR("seek offset not aligned to sizeof(Directory_entry)"); return 0; } seek_off_t index = seek_offset / sizeof(Directory_entry); int bytes; rump_sys_lseek(_fd, 0, SEEK_SET); struct dirent *dent = 0; seek_off_t i = 0; char *buf = _buffer(); do { bytes = rump_sys_getdents(_fd, buf, BUFFER_SIZE); void *current, *end; for (current = buf, end = &buf[bytes]; current < end; current = _DIRENT_NEXT((dirent *)current), i++) if (i == index) { dent = (dirent *)current; break; } } while(bytes && !dent); if (!dent) return 0; /* * Build absolute path, this becomes necessary as our 'Path' class strips * trailing dots, which will not work for '.' and '..' directories. */ size_t base_len = strlen(_path.base()); char path[dent->d_namlen + base_len + 2]; memcpy(path, _path.base(), base_len); path[base_len] = '/'; strncpy(path + base_len + 1, dent->d_name, dent->d_namlen + 1); /* * We cannot use 'd_type' member of 'dirent' here since the EXT2 * implementation sets the type to unkown. Hence we use stat. */ struct stat s; rump_sys_lstat(path, &s); Directory_entry *e = (Directory_entry *)(dst); if (S_ISDIR(s.st_mode)) e->type = Directory_entry::TYPE_DIRECTORY; else if (S_ISREG(s.st_mode)) e->type = Directory_entry::TYPE_FILE; else if (S_ISLNK(s.st_mode)) e->type = Directory_entry::TYPE_SYMLINK; else return 0; strncpy(e->name, dent->d_name, dent->d_namlen + 1); return sizeof(Directory_entry); } size_t write(char const *src, size_t len, seek_off_t seek_offset) { /* writing to directory nodes is not supported */ return 0; } size_t num_entries() const { int bytes = 0; int count = 0; rump_sys_lseek(_fd, 0, SEEK_SET); char *buf = _buffer(); do { bytes = rump_sys_getdents(_fd, buf, BUFFER_SIZE); void *current, *end; for (current = buf, end = &buf[bytes]; current < end; current = _DIRENT_NEXT((dirent *)current), count++) { } } while(bytes); return count; } void unlink(char const *path) { Path node_path(path, _path.base()); struct stat s; int ret = rump_sys_lstat(node_path.base(), &s); if (ret == -1) throw Lookup_failed(); if (S_ISDIR(s.st_mode)) ret = rump_sys_rmdir(node_path.base()); else if (S_ISREG(s.st_mode) || S_ISLNK(s.st_mode)) ret = rump_sys_unlink(node_path.base()); else throw Lookup_failed(); if (ret == -1) PERR("Error during unlink of %s", node_path.base()); } }; #endif /* _DIRECTORY_H_ */