Goto sanos source index
//
// dfs.c
//
// Disk filesystem routines
//
// Copyright (C) 2002 Michael Ringgaard. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// 3. Neither the name of the project nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE.
//
#include <os/krnl.h>
struct fsops dfsops = {
FSOP_READ | FSOP_WRITE | FSOP_IOCTL | FSOP_TELL | FSOP_LSEEK | FSOP_FTRUNCATE |
FSOP_FUTIME | FSOP_FSTAT,
NULL,
NULL,
dfs_mkfs,
dfs_mount,
dfs_umount,
dfs_statfs,
dfs_open,
dfs_close,
dfs_destroy,
dfs_fsync,
dfs_read,
dfs_write,
dfs_ioctl,
dfs_tell,
dfs_lseek,
dfs_ftruncate,
dfs_futime,
dfs_utime,
dfs_fstat,
dfs_stat,
dfs_access,
dfs_fchmod,
dfs_chmod,
dfs_fchown,
dfs_chown,
dfs_mkdir,
dfs_rmdir,
dfs_rename,
dfs_link,
dfs_unlink,
dfs_opendir,
dfs_readdir
};
void init_dfs() {
register_filesystem("dfs", &dfsops);
}
int dfs_utime(struct fs *fs, char *name, struct utimbuf *times) {
struct inode *inode;
int rc;
rc = namei((struct filsys *) fs->data, name, &inode);
if (rc < 0) return rc;
rc = checki(inode, S_IWRITE);
if (rc < 0) {
release_inode(inode);
return rc;
}
if (times->ctime != -1) inode->desc->ctime = times->ctime;
if (times->modtime != -1) inode->desc->mtime = times->modtime;
mark_inode_dirty(inode);
release_inode(inode);
return 0;
}
int dfs_stat(struct fs *fs, char *name, struct stat64 *buffer) {
struct inode *inode;
off64_t size;
int rc;
rc = namei((struct filsys *) fs->data, name, &inode);
if (rc < 0) return rc;
size = inode->desc->size;
if (buffer) {
memset(buffer, 0, sizeof(struct stat64));
buffer->st_mode = inode->desc->mode;
buffer->st_uid = inode->desc->uid;
buffer->st_gid = inode->desc->gid;
buffer->st_ino = inode->ino;
buffer->st_nlink = inode->desc->linkcount;
buffer->st_dev = ((struct filsys *) fs->data)->devno;
buffer->st_atime = time(NULL);
buffer->st_mtime = inode->desc->mtime;
buffer->st_ctime = inode->desc->ctime;
buffer->st_size = inode->desc->size;
}
release_inode(inode);
return (int) size;
}
int dfs_access(struct fs *fs, char *name, int mode) {
struct thread *thread = self();
struct inode *inode;
int rc;
rc = namei((struct filsys *) fs->data, name, &inode);
if (rc < 0) return rc;
if (mode != 0) {
if (thread->euid == 0) {
rc = mode != 1 || inode->desc->mode & 0111 ? 0 : -EACCES;
} else {
if (thread->euid != inode->desc->uid) {
mode >>= 3;
if (thread->egid != inode->desc->gid) mode >>= 3;
}
if ((mode & inode->desc->mode) == 0) rc = -EACCES;
}
}
release_inode(inode);
return rc;
}
int dfs_mkdir(struct fs *fs, char *name, int mode) {
struct inode *parent;
ino_t ino;
struct inode *dir;
int len;
int rc;
len = strlen(name);
if (len == 0) return -EEXIST;
rc = diri((struct filsys *) fs->data, &name, &len, &parent);
if (rc < 0) return rc;
rc = find_dir_entry(parent, name, len, &ino);
if (rc != -ENOENT) {
release_inode(parent);
return rc >= 0 ? -EEXIST : rc;
}
dir = alloc_inode(parent, S_IFDIR | (mode & S_IRWXUGO));
if (!dir) {
release_inode(parent);
return -ENOSPC;
}
rc = add_dir_entry(parent, name, len, dir->ino);
if (rc < 0) {
unlink_inode(dir);
release_inode(dir);
release_inode(parent);
return rc;
}
release_inode(dir);
release_inode(parent);
return 0;
}
int dfs_rmdir(struct fs *fs, char *name) {
struct inode *parent;
struct inode *dir;
ino_t ino;
int len;
int rc;
len = strlen(name);
rc = diri((struct filsys *) fs->data, &name, &len, &parent);
if (rc < 0) return rc;
rc = find_dir_entry(parent, name, len, &ino);
if (rc < 0) {
release_inode(parent);
return rc;
}
if (ino == DFS_INODE_ROOT) {
release_inode(parent);
return -EPERM;
}
rc = get_inode(parent->fs, ino, &dir);
if (rc < 0) {
release_inode(parent);
return rc;
}
if (!S_ISDIR(dir->desc->mode)) {
release_inode(dir);
release_inode(parent);
return -ENOTDIR;
}
if (dir->desc->linkcount == 1) {
if (dir->desc->size > 0) {
release_inode(dir);
release_inode(parent);
return -ENOTEMPTY;
}
rc = delete_dir_entry(parent, name, len);
if (rc < 0) {
release_inode(dir);
release_inode(parent);
return rc;
}
}
rc = unlink_inode(dir);
if (rc < 0) {
release_inode(dir);
release_inode(parent);
return rc;
}
release_inode(dir);
release_inode(parent);
return 0;
}
int dfs_rename(struct fs *fs, char *oldname, char *newname) {
struct inode *oldparent;
struct inode *newparent;
int oldlen;
int newlen;
ino_t ino;
int rc;
oldlen = strlen(oldname);
rc = diri((struct filsys *) fs->data, &oldname, &oldlen, &oldparent);
if (rc < 0) return rc;
rc = find_dir_entry(oldparent, oldname, oldlen, &ino);
if (rc < 0) {
release_inode(oldparent);
return rc;
}
newlen = strlen(newname);
rc = diri((struct filsys *) fs->data, &newname, &newlen, &newparent);
if (rc < 0) {
release_inode(oldparent);
return rc;
}
rc = find_dir_entry(newparent, newname, newlen, NULL);
if (rc != -ENOENT) {
release_inode(oldparent);
release_inode(newparent);
if (strcmp(oldname, newname) == 0) return 0;
return rc >= 0 ? -EEXIST : rc;
}
if (checki(oldparent, S_IWRITE) < 0 || checki(newparent, S_IWRITE) < 0) {
release_inode(oldparent);
release_inode(newparent);
return -EACCES;
}
rc = add_dir_entry(newparent, newname, newlen, ino);
if (rc < 0) {
release_inode(oldparent);
release_inode(newparent);
return rc;
}
rc = delete_dir_entry(oldparent, oldname, oldlen);
if (rc < 0) {
release_inode(oldparent);
release_inode(newparent);
return rc;
}
release_inode(oldparent);
release_inode(newparent);
return 0;
}
int dfs_link(struct fs *fs, char *oldname, char *newname) {
struct inode *inode;
struct inode *parent;
int len;
int rc;
rc = namei((struct filsys *) fs->data, oldname, &inode);
if (rc < 0) return rc;
len = strlen(newname);
rc = diri((struct filsys *) fs->data, &newname, &len, &parent);
if (rc < 0) {
release_inode(inode);
return rc;
}
rc = find_dir_entry(parent, newname, len, NULL);
if (rc != -ENOENT) {
release_inode(inode);
release_inode(parent);
return rc >= 0 ? -EEXIST : rc;
}
inode->desc->linkcount++;
mark_inode_dirty(inode);
rc = add_dir_entry(parent, newname, len, inode->ino);
if (rc < 0) {
unlink_inode(inode);
release_inode(inode);
release_inode(parent);
return rc;
}
release_inode(inode);
release_inode(parent);
return 0;
}
int dfs_unlink(struct fs *fs, char *name) {
struct inode *dir;
struct inode *inode;
ino_t ino;
int len;
int rc;
len = strlen(name);
rc = diri((struct filsys *) fs->data, &name, &len, &dir);
if (rc < 0) return rc;
rc = find_dir_entry(dir, name, len, &ino);
if (rc < 0) {
release_inode(dir);
return rc;
}
rc = get_inode(dir->fs, ino, &inode);
if (rc < 0) {
release_inode(dir);
return rc;
}
if (S_ISDIR(inode->desc->mode)) {
release_inode(inode);
release_inode(dir);
return -EISDIR;
}
rc = delete_dir_entry(dir, name, len);
if (rc < 0) {
release_inode(inode);
release_inode(dir);
return rc;
}
rc = unlink_inode(inode);
if (rc < 0) {
release_inode(inode);
release_inode(dir);
return rc;
}
release_inode(inode);
release_inode(dir);
return 0;
}
int dfs_chmod(struct fs *fs, char *name, int mode) {
struct thread *thread = self();
struct inode *inode;
int rc;
rc = namei((struct filsys *) fs->data, name, &inode);
if (rc < 0) return rc;
if (thread->euid != 0 && thread->euid != inode->desc->uid) {
release_inode(inode);
return -EPERM;
}
inode->desc->mode = (inode->desc->mode & ~S_IRWXUGO) | (mode & S_IRWXUGO);
mark_inode_dirty(inode);
release_inode(inode);
return 0;
}
int dfs_chown(struct fs *fs, char *name, int owner, int group) {
struct thread *thread = self();
struct inode *inode;
int rc;
rc = namei((struct filsys *) fs->data, name, &inode);
if (rc < 0) return rc;
if (thread->euid != 0) {
release_inode(inode);
return -EPERM;
}
if (owner != -1) inode->desc->uid = owner;
if (group != -1) inode->desc->gid = group;
mark_inode_dirty(inode);
release_inode(inode);
return 0;
}