From: Nadav Har'El <n...@scylladb.com>
Committer: Nadav Har'El <n...@scylladb.com>
Branch: master
ramfs: support symbolic links
This patch adds the missing support in ramfs for creating and reading
symbolic links, and then modifies the "bootfs" format (which we use to
pack and unpack the ram disk files) to also allow symbolic links.
After this patch, the following works:
scripts/build image=cli fs=ramfs
Fixes #871
Signed-off-by: Nadav Har'El <n...@scylladb.com>
Message-Id: <20170425225010.24418-2-...@scylladb.com>
---
diff --git a/fs/ramfs/ramfs_vnops.cc b/fs/ramfs/ramfs_vnops.cc
--- a/fs/ramfs/ramfs_vnops.cc
+++ b/fs/ramfs/ramfs_vnops.cc
@@ -224,6 +224,42 @@ ramfs_mkdir(struct vnode *dvp, char *name, mode_t mode)
return 0;
}
+static int
+ramfs_symlink(struct vnode *dvp, char *name, char *link)
+{
+ auto np = ramfs_add_node((ramfs_node*)dvp->v_data, name, VLNK);
+ if (np == NULL)
+ return ENOMEM;
+ // Save the link target without the final null, as readlink() wants it.
+ size_t len = strlen(link);
+ np->rn_buf = strndup(link, len);
+ np->rn_bufsize = np->rn_size = len;
+ return 0;
+}
+
+static int
+ramfs_readlink(struct vnode *vp, struct uio *uio) {
+ struct ramfs_node *np = (ramfs_node*)vp->v_data;
+ size_t len;
+
+ if (vp->v_type != VLNK) {
+ return EINVAL;
+ }
+ if (uio->uio_offset < 0) {
+ return EINVAL;
+ }
+ if (uio->uio_resid == 0) {
+ return 0;
+ }
+ if (uio->uio_offset >= (off_t)vp->v_size)
+ return 0;
+ if (vp->v_size - uio->uio_offset < uio->uio_resid)
+ len = vp->v_size - uio->uio_offset;
+ else
+ len = uio->uio_resid;
+ return uiomove(np->rn_buf + uio->uio_offset, len, uio);
+}
+
/* Remove a directory */
static int
ramfs_rmdir(struct vnode *dvp, struct vnode *vp, char *name)
@@ -388,11 +424,11 @@ ramfs_rename(struct vnode *dvp1, struct vnode *vp1,
char *name1,
} else {
/* Create new file or directory */
old_np = (ramfs_node*)vp1->v_data;
- np = ramfs_add_node((ramfs_node*)dvp2->v_data, name2, VREG);
+ np = ramfs_add_node((ramfs_node*)dvp2->v_data, name2,
old_np->rn_type);
if (np == NULL)
return ENOMEM;
- if (vp1->v_type == VREG) {
+ if (old_np->rn_buf) {
/* Copy file data */
np->rn_buf = old_np->rn_buf;
np->rn_size = old_np->rn_size;
@@ -439,6 +475,8 @@ ramfs_readdir(struct vnode *vp, struct file *fp, struct
dirent *dir)
}
if (np->rn_type == VDIR)
dir->d_type = DT_DIR;
+ else if (np->rn_type == VLNK)
+ dir->d_type = DT_LNK;
else
dir->d_type = DT_REG;
strlcpy((char *)&dir->d_name, np->rn_name,
@@ -476,8 +514,6 @@ ramfs_getattr(struct vnode *vnode, struct vattr *attr)
#define ramfs_inactive ((vnop_inactive_t)vop_nullop)
#define ramfs_link ((vnop_link_t)vop_eperm)
#define ramfs_fallocate ((vnop_fallocate_t)vop_nullop)
-#define ramfs_readlink ((vnop_readlink_t)vop_nullop)
-#define ramfs_symlink ((vnop_symlink_t)vop_nullop)
/*
* vnode operations
diff --git a/fs/vfs/main.cc b/fs/vfs/main.cc
--- a/fs/vfs/main.cc
+++ b/fs/vfs/main.cc
@@ -2100,11 +2100,21 @@ int chroot(const char *path)
return -1;
}
-#define BOOTFS_PATH_MAX 112
-
+// unpack_bootfs() unpacks a collection of files stored as part of the OSv
+// executable (in memory location "bootfs_start") into the file system,
+// normally the in-memory filesystem ramfs.
+// The files are packed in the executable in an ad-hoc format defined here.
+// Code in scripts/mkbootfs.py packs files into this format.
+#define BOOTFS_PATH_MAX 111
+enum class bootfs_file_type : char { other = 0, symlink = 1 };
struct bootfs_metadata {
uint64_t size;
uint64_t offset;
+ // The file's type. Can be "symlink" or "other". A directory is
an "other"
+ // file with its name ending with a "/" (and no content).
+ bootfs_file_type type;
+ // name must end with a null. For symlink files, the content must end
+ // with a null as well.
char name[BOOTFS_PATH_MAX];
};
@@ -2130,6 +2140,15 @@ void unpack_bootfs(void)
}
}
+ if (md[i].type == bootfs_file_type::symlink) {
+ // This is a symbolic link record. The file's content is the
+ // target path, and we assume ends with a null.
+ if (symlink(&bootfs_start + md[i].offset, md[i].name) != 0) {
+ kprintf("couldn't symlink %s: %d\n", md[i].name, errno);
+ sys_panic("unpack_bootfs failed");
+ }
+ continue;
+ }
if (*(p-1) == '/' && md[i].size == 0) {
// This is directory record. Nothing else to do
continue;
@@ -2206,6 +2225,9 @@ static void import_extra_zfs_pools(void)
// Import extra pools mounting datasets there contained.
// Datasets from osv pool will not be mounted here.
+ if (access("zpool.so", X_OK) != 0) {
+ return;
+ }
vector<string> zpool_args = {"zpool", "import", "-f", "-a" };
auto ok = osv::run("zpool.so", zpool_args, &ret);
assert(ok);
diff --git a/scripts/mkbootfs.py b/scripts/mkbootfs.py
--- a/scripts/mkbootfs.py
+++ b/scripts/mkbootfs.py
@@ -96,6 +96,7 @@ def main():
(options, args) = opt.parse_args()
+ # See unpack_bootfs() as the reference to this ad-hoc packing format.
metadata_size = 128
depends = io.StringIO()
if options.depends:
@@ -113,17 +114,21 @@ def main():
pos = (len(files) + 1) * metadata_size
for name, hostname in files:
+ type = 0
if hostname.startswith("->"):
- raise Exception("Symlinks in bootfs are not supported")
-
- if os.path.isdir(hostname):
+ link = hostname[2:]
+ type = 1
+ size = len(link.encode())+1
+ elif os.path.isdir(hostname):
size = 0;
if not name.endswith("/"):
name += "/"
else:
size = os.stat(hostname).st_size
- metadata = struct.pack('QQ112s', size, pos, name.encode())
+ # FIXME: check if name.encode()'s length is more than 110 (111
+ # minus the necessary null byte) and fail if it is.
+ metadata = struct.pack('QQb111s', size, pos, type, name.encode())
out.write(metadata)
pos += size
depends.write(u'\t%s \\\n' % (hostname,))
@@ -133,7 +138,12 @@ def main():
for name, hostname in files:
if os.path.isdir(hostname):
continue
- out.write(open(hostname, 'rb').read())
+ if hostname.startswith("->"):
+ link = hostname[2:]
+ out.write(link.encode())
+ out.write('\0')
+ else:
+ out.write(open(hostname, 'rb').read())
depends.write(u'\n\n')
diff --git a/usr_nozfs.manifest.skel b/usr_nozfs.manifest.skel
--- a/usr_nozfs.manifest.skel
+++ b/usr_nozfs.manifest.skel
@@ -5,8 +5,7 @@
/usr/lib/libgcc_s.so.1: %(gccbase)s/lib64/libgcc_s.so.1
/&/etc/hosts: ../../static/&
-# TODO: symlinks are not supported by ramfs
-#/etc/mnttab: ->/proc/mounts
+/etc/mnttab: ->/proc/mounts
/&/etc/fstab: ../../static/&
/dev: ../../static
/proc: ../../static
--
You received this message because you are subscribed to the Google Groups "OSv
Development" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to osv-dev+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.