summaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2015-12-10 09:31:02 +0100
committerSascha Hauer <s.hauer@pengutronix.de>2016-01-08 08:30:55 +0100
commit8a35fddf572578eea4846aa440d3caba31f3cc37 (patch)
treef82987667b56c3ea4403161f4187a9d1f8deb7bb /fs
parent3cff8948ff5d2d4dc834bdc955684b73571713bd (diff)
downloadbarebox-8a35fddf572578eea4846aa440d3caba31f3cc37.tar.gz
barebox-8a35fddf572578eea4846aa440d3caba31f3cc37.tar.xz
devfs: Add symlink support
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'fs')
-rw-r--r--fs/devfs-core.c76
-rw-r--r--fs/devfs.c27
2 files changed, 89 insertions, 14 deletions
diff --git a/fs/devfs-core.c b/fs/devfs-core.c
index a5ac5a3843..88a7e3a1d6 100644
--- a/fs/devfs-core.c
+++ b/fs/devfs-core.c
@@ -46,7 +46,18 @@ int devfs_partition_complete(struct string_list *sl, char *instr)
}
#endif
-struct cdev *cdev_by_name(const char *filename)
+struct cdev *cdev_readlink(struct cdev *cdev)
+{
+ if (cdev->link)
+ cdev = cdev->link;
+
+ /* links to links are not allowed */
+ BUG_ON(cdev->link);
+
+ return cdev;
+}
+
+struct cdev *lcdev_by_name(const char *filename)
{
struct cdev *cdev;
@@ -57,6 +68,17 @@ struct cdev *cdev_by_name(const char *filename)
return NULL;
}
+struct cdev *cdev_by_name(const char *filename)
+{
+ struct cdev *cdev;
+
+ cdev = lcdev_by_name(filename);
+ if (!cdev)
+ return NULL;
+
+ return cdev_readlink(cdev);
+}
+
struct cdev *cdev_by_device_node(struct device_node *node)
{
struct cdev *cdev;
@@ -65,7 +87,7 @@ struct cdev *cdev_by_device_node(struct device_node *node)
if (!cdev->device_node)
continue;
if (cdev->device_node == node)
- return cdev;
+ return cdev_readlink(cdev);
}
return NULL;
}
@@ -111,14 +133,6 @@ int cdev_find_free_index(const char *basename)
return -EBUSY; /* all indexes are used */
}
-int cdev_do_open(struct cdev *cdev, unsigned long flags)
-{
- if (cdev->ops->open)
- return cdev->ops->open(cdev, flags);
-
- return 0;
-}
-
struct cdev *cdev_open(const char *name, unsigned long flags)
{
struct cdev *cdev;
@@ -131,9 +145,11 @@ struct cdev *cdev_open(const char *name, unsigned long flags)
if (!cdev)
return NULL;
- ret = cdev_do_open(cdev, flags);
- if (ret)
- return NULL;
+ if (cdev->ops->open) {
+ ret = cdev->ops->open(cdev, flags);
+ if (ret)
+ return NULL;
+ }
return cdev;
}
@@ -259,6 +275,8 @@ int devfs_create(struct cdev *new)
if (cdev)
return -EEXIST;
+ INIT_LIST_HEAD(&new->links);
+
list_add_tail(&new->list, &cdev_list);
if (new->dev) {
list_add_tail(&new->devices_list, &new->dev->cdevs);
@@ -269,15 +287,47 @@ int devfs_create(struct cdev *new)
return 0;
}
+int devfs_create_link(struct cdev *cdev, const char *name)
+{
+ struct cdev *new;
+
+ if (cdev_by_name(name))
+ return -EEXIST;
+
+ /*
+ * Create a link to the real cdev instead of creating
+ * a link to a link.
+ */
+ cdev = cdev_readlink(cdev);
+
+ new = xzalloc(sizeof(*new));
+ new->name = xstrdup(name);
+ new->link = cdev;
+ INIT_LIST_HEAD(&new->links);
+ list_add_tail(&new->list, &cdev_list);
+ list_add_tail(&new->link_entry, &cdev->links);
+
+ return 0;
+}
+
int devfs_remove(struct cdev *cdev)
{
+ struct cdev *c, *tmp;
+
if (cdev->open)
return -EBUSY;
list_del(&cdev->list);
+
if (cdev->dev)
list_del(&cdev->devices_list);
+ list_for_each_entry_safe(c, tmp, &cdev->links, link_entry)
+ devfs_remove(c);
+
+ if (cdev->link)
+ free(cdev);
+
return 0;
}
diff --git a/fs/devfs.c b/fs/devfs.c
index 5c96682f96..0b8d4fd246 100644
--- a/fs/devfs.c
+++ b/fs/devfs.c
@@ -212,12 +212,18 @@ static int devfs_stat(struct device_d *_dev, const char *filename, struct stat *
{
struct cdev *cdev;
- cdev = cdev_by_name(filename + 1);
+ cdev = lcdev_by_name(filename + 1);
if (!cdev)
return -ENOENT;
s->st_mode = S_IFCHR;
s->st_size = cdev->size;
+
+ if (cdev->link)
+ s->st_mode |= S_IFLNK;
+
+ cdev = cdev_readlink(cdev);
+
if (cdev->ops->write)
s->st_mode |= S_IWUSR;
if (cdev->ops->read)
@@ -242,6 +248,24 @@ static void devfs_delete(struct device_d *dev)
{
}
+static int devfs_readlink(struct device_d *dev, const char *pathname,
+ char *buf, size_t bufsz)
+{
+ struct cdev *cdev;
+
+ cdev = cdev_by_name(pathname + 1);
+ if (!cdev)
+ return -ENOENT;
+
+ while (cdev->link)
+ cdev = cdev->link;
+
+ bufsz = min(bufsz, strlen(cdev->name));
+ memcpy(buf, cdev->name, bufsz);
+
+ return 0;
+}
+
static struct fs_driver_d devfs_driver = {
.read = devfs_read,
.write = devfs_write,
@@ -258,6 +282,7 @@ static struct fs_driver_d devfs_driver = {
.erase = devfs_erase,
.protect = devfs_protect,
.memmap = devfs_memmap,
+ .readlink = devfs_readlink,
.flags = FS_DRIVER_NO_DEV,
.drv = {
.probe = devfs_probe,