summaryrefslogtreecommitdiffstats
path: root/fs/legacy.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/legacy.c')
-rw-r--r--fs/legacy.c315
1 files changed, 315 insertions, 0 deletions
diff --git a/fs/legacy.c b/fs/legacy.c
new file mode 100644
index 0000000000..fc6a18f408
--- /dev/null
+++ b/fs/legacy.c
@@ -0,0 +1,315 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+#include <common.h>
+#include <fs.h>
+
+static struct inode *legacy_get_inode(struct super_block *sb, const struct inode *dir,
+ umode_t mode);
+
+static int legacy_iterate(struct file *file, struct dir_context *ctx)
+{
+ struct dentry *dentry = file->f_path.dentry;
+ struct inode *dir = d_inode(dentry);
+ struct super_block *sb = dir->i_sb;
+ struct fs_device_d *fsdev = container_of(sb, struct fs_device_d, sb);
+ struct dir *d;
+ struct dirent *dirent;
+ char *pathname;
+
+ dir_emit_dots(file, ctx);
+
+ pathname = dpath(dentry, fsdev->vfsmount.mnt_root);
+
+ d = fsdev->driver->opendir(&fsdev->dev, pathname);
+ while (1) {
+ dirent = fsdev->driver->readdir(&fsdev->dev, d);
+ if (!dirent)
+ break;
+
+ dir_emit(ctx, dirent->d_name, strlen(dirent->d_name), 0, DT_UNKNOWN);
+ }
+
+ fsdev->driver->closedir(&fsdev->dev, d);
+
+ free(pathname);
+
+ return 0;
+}
+
+static struct dentry *legacy_lookup(struct inode *dir, struct dentry *dentry,
+ unsigned int flags)
+{
+ struct super_block *sb = dir->i_sb;
+ struct fs_device_d *fsdev = container_of(sb, struct fs_device_d, sb);
+ struct inode *inode;
+ char *pathname;
+ struct stat s;
+ int ret;
+
+ pathname = dpath(dentry, fsdev->vfsmount.mnt_root);
+
+ ret = fsdev->driver->stat(&fsdev->dev, pathname, &s);
+ if (!ret) {
+ inode = legacy_get_inode(sb, dir, s.st_mode);
+
+ inode->i_size = s.st_size;
+ inode->i_mode = s.st_mode;
+
+ d_add(dentry, inode);
+ }
+
+ return NULL;
+}
+
+static int legacy_create(struct inode *dir, struct dentry *dentry, umode_t mode)
+{
+ struct super_block *sb = dir->i_sb;
+ struct fs_device_d *fsdev = container_of(sb, struct fs_device_d, sb);
+ struct inode *inode;
+ char *pathname;
+ int ret;
+
+ if (!fsdev->driver->create)
+ return -EROFS;
+
+ pathname = dpath(dentry, fsdev->vfsmount.mnt_root);
+
+ ret = fsdev->driver->create(&fsdev->dev, pathname, mode | S_IFREG);
+
+ free(pathname);
+
+ if (ret)
+ return ret;
+
+ inode = legacy_get_inode(sb, dir, mode | S_IFREG);
+
+ d_instantiate(dentry, inode);
+
+ return 0;
+}
+
+static int legacy_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
+{
+ struct super_block *sb = dir->i_sb;
+ struct fs_device_d *fsdev = container_of(sb, struct fs_device_d, sb);
+ struct inode *inode;
+ char *pathname;
+ int ret;
+
+ if (!fsdev->driver->mkdir)
+ return -EROFS;
+
+ pathname = dpath(dentry, fsdev->vfsmount.mnt_root);
+
+ ret = fsdev->driver->mkdir(&fsdev->dev, pathname);
+
+ free(pathname);
+
+ if (ret)
+ return ret;
+
+ inode = legacy_get_inode(sb, dir, mode | S_IFDIR);
+
+ d_instantiate(dentry, inode);
+
+ return 0;
+}
+
+static int legacy_dir_is_empty(struct dentry *dentry)
+{
+ struct inode *dir = d_inode(dentry);
+ struct super_block *sb = dir->i_sb;
+ struct fs_device_d *fsdev = container_of(sb, struct fs_device_d, sb);
+ struct dir *d;
+ struct dirent *dirent;
+ char *pathname;
+
+ pathname = dpath(dentry, fsdev->vfsmount.mnt_root);
+
+ d = fsdev->driver->opendir(&fsdev->dev, pathname);
+ dirent = fsdev->driver->readdir(&fsdev->dev, d);
+
+ fsdev->driver->closedir(&fsdev->dev, d);
+
+ free(pathname);
+
+ return dirent ? 0 : 1;
+}
+
+static int legacy_rmdir(struct inode *dir, struct dentry *dentry)
+{
+ struct super_block *sb = dir->i_sb;
+ struct fs_device_d *fsdev = container_of(sb, struct fs_device_d, sb);
+ char *pathname;
+ int ret;
+
+ if (!fsdev->driver->rmdir)
+ return -EROFS;
+
+ if (!legacy_dir_is_empty(dentry))
+ return -ENOTEMPTY;
+
+ pathname = dpath(dentry, fsdev->vfsmount.mnt_root);
+
+ ret = fsdev->driver->rmdir(&fsdev->dev, pathname);
+
+ free(pathname);
+
+ if (ret)
+ return ret;
+
+ drop_nlink(d_inode(dentry));
+ dput(dentry);
+ drop_nlink(dir);
+
+ return 0;
+}
+
+static int legacy_unlink(struct inode *dir, struct dentry *dentry)
+{
+ struct super_block *sb = dir->i_sb;
+ struct fs_device_d *fsdev = container_of(sb, struct fs_device_d, sb);
+ char *pathname;
+ int ret;
+
+ if (!fsdev->driver->unlink)
+ return -EROFS;
+
+ pathname = dpath(dentry, fsdev->vfsmount.mnt_root);
+
+ ret = fsdev->driver->unlink(&fsdev->dev, pathname);
+
+ free(pathname);
+
+ if (ret)
+ return ret;
+
+ drop_nlink(d_inode(dentry));
+ dput(dentry);
+
+ return 0;
+}
+
+static int legacy_symlink(struct inode *dir, struct dentry *dentry,
+ const char *dest)
+{
+ struct super_block *sb = dir->i_sb;
+ struct fs_device_d *fsdev = container_of(sb, struct fs_device_d, sb);
+ struct inode *inode;
+ char *pathname;
+ int ret;
+
+ if (!fsdev->driver->symlink)
+ return -ENOSYS;
+
+ pathname = dpath(dentry, fsdev->vfsmount.mnt_root);
+
+ ret = fsdev->driver->symlink(&fsdev->dev, dest, pathname);
+
+ free(pathname);
+
+ if (ret)
+ return ret;
+
+ inode = legacy_get_inode(sb, dir, S_IFLNK);
+ inode->i_link = xstrdup(dest);
+
+ d_instantiate(dentry, inode);
+
+ return 0;
+}
+
+static const char *legacy_get_link(struct dentry *dentry, struct inode *inode)
+{
+ struct super_block *sb = inode->i_sb;
+ struct fs_device_d *fsdev = container_of(sb, struct fs_device_d, sb);
+ char *pathname;
+ int ret;
+ char link[PATH_MAX] = {};
+
+ if (!fsdev->driver->readlink)
+ return ERR_PTR(-ENOSYS);
+
+ pathname = dpath(dentry, fsdev->vfsmount.mnt_root);
+
+ ret = fsdev->driver->readlink(&fsdev->dev, pathname, link, PATH_MAX - 1);
+
+ free(pathname);
+
+ if (ret)
+ return NULL;
+
+ inode->i_link = xstrdup(link);
+
+ return inode->i_link;
+}
+
+static const struct super_operations legacy_s_ops;
+static const struct inode_operations legacy_file_inode_operations;
+
+static const struct inode_operations legacy_dir_inode_operations = {
+ .lookup = legacy_lookup,
+ .create = legacy_create,
+ .mkdir = legacy_mkdir,
+ .rmdir = legacy_rmdir,
+ .unlink = legacy_unlink,
+ .symlink = legacy_symlink,
+};
+
+static const struct file_operations legacy_dir_operations = {
+ .iterate = legacy_iterate,
+};
+
+static const struct inode_operations legacy_symlink_inode_operations = {
+ .get_link = legacy_get_link,
+};
+
+static struct inode *legacy_get_inode(struct super_block *sb, const struct inode *dir,
+ umode_t mode)
+{
+ struct inode *inode = new_inode(sb);
+
+ if (!inode)
+ return NULL;
+
+ inode->i_ino = get_next_ino();
+ inode->i_mode = mode;
+
+ switch (mode & S_IFMT) {
+ default:
+ return NULL;
+ case S_IFREG:
+ case S_IFCHR:
+ inode->i_op = &legacy_file_inode_operations;
+ break;
+ case S_IFDIR:
+ inode->i_op = &legacy_dir_inode_operations;
+ inode->i_fop = &legacy_dir_operations;
+ inc_nlink(inode);
+ break;
+ case S_IFLNK:
+ inode->i_op = &legacy_symlink_inode_operations;
+ break;
+ }
+
+ return inode;
+}
+
+int fs_init_legacy(struct fs_device_d *fsdev)
+{
+ struct inode *inode;
+
+ fsdev->sb.s_op = &legacy_s_ops;
+ inode = legacy_get_inode(&fsdev->sb, NULL, S_IFDIR);
+ fsdev->sb.s_root = d_make_root(inode);
+
+ return 0;
+}