summaryrefslogtreecommitdiffstats
path: root/fs/devfs-core.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/devfs-core.c')
-rw-r--r--fs/devfs-core.c203
1 files changed, 155 insertions, 48 deletions
diff --git a/fs/devfs-core.c b/fs/devfs-core.c
index 38423e5d1e..376c62be9e 100644
--- a/fs/devfs-core.c
+++ b/fs/devfs-core.c
@@ -38,7 +38,7 @@ int devfs_partition_complete(struct string_list *sl, char *instr)
len = strlen(instr);
for_each_cdev(cdev) {
- if (cdev->master &&
+ if (cdev_is_partition(cdev) &&
!strncmp(instr, cdev->name, len)) {
string_list_add_asprintf(sl, "%s ", cdev->name);
}
@@ -49,6 +49,9 @@ int devfs_partition_complete(struct string_list *sl, char *instr)
struct cdev *cdev_readlink(struct cdev *cdev)
{
+ if (!cdev)
+ return NULL;
+
if (cdev->link)
cdev = cdev->link;
@@ -84,10 +87,11 @@ struct cdev *cdev_by_device_node(struct device_node *node)
{
struct cdev *cdev;
+ if (!node)
+ return NULL;
+
for_each_cdev(cdev) {
- if (!cdev->device_node)
- continue;
- if (cdev->device_node == node)
+ if (cdev_of_node(cdev) == node)
return cdev_readlink(cdev);
}
return NULL;
@@ -101,7 +105,7 @@ struct cdev *cdev_by_partuuid(const char *partuuid)
return NULL;
for_each_cdev(cdev) {
- if (cdev->master && !strcasecmp(cdev->uuid, partuuid))
+ if (cdev_is_partition(cdev) && !strcasecmp(cdev->partuuid, partuuid))
return cdev;
}
return NULL;
@@ -115,7 +119,7 @@ struct cdev *cdev_by_diskuuid(const char *diskuuid)
return NULL;
for_each_cdev(cdev) {
- if (!cdev->master && !strcasecmp(cdev->uuid, diskuuid))
+ if (!cdev_is_partition(cdev) && !strcasecmp(cdev->diskuuid, diskuuid))
return cdev;
}
return NULL;
@@ -127,10 +131,10 @@ struct cdev *cdev_by_diskuuid(const char *diskuuid)
* @dev: the device which should be searched for partitions
* @name: the partition name
*/
-struct cdev *device_find_partition(struct device_d *dev, const char *name)
+struct cdev *device_find_partition(struct device *dev, const char *name)
{
struct cdev *cdev;
- struct device_d *child;
+ struct device *child;
list_for_each_entry(cdev, &dev->cdevs, devices_list) {
struct cdev *cdevl;
@@ -171,12 +175,39 @@ int cdev_find_free_index(const char *basename)
int cdev_open(struct cdev *cdev, unsigned long flags)
{
- if (cdev->ops->open)
- return cdev->ops->open(cdev, flags);
+ int ret;
+
+ if (cdev->ops->open) {
+ ret = cdev->ops->open(cdev, flags);
+ if (ret)
+ return ret;
+ }
+
+ cdev->open++;
return 0;
}
+int cdev_fdopen(struct cdev *cdev, unsigned long flags)
+{
+ char *path;
+ int fd;
+
+ if (!cdev)
+ return -ENODEV;
+ if (IS_ERR(cdev))
+ return PTR_ERR(cdev);
+
+ path = basprintf("/dev/%s", cdev->name);
+ if (!path)
+ return -ENOMEM;
+
+ fd = open(path, flags);
+
+ free(path);
+ return fd;
+}
+
struct cdev *cdev_open_by_name(const char *name, unsigned long flags)
{
struct cdev *cdev;
@@ -197,6 +228,8 @@ void cdev_close(struct cdev *cdev)
{
if (cdev->ops->close)
cdev->ops->close(cdev);
+
+ cdev->open--;
}
ssize_t cdev_read(struct cdev *cdev, void *buf, size_t count, loff_t offset, ulong flags)
@@ -300,6 +333,16 @@ int cdev_truncate(struct cdev *cdev, size_t size)
return -EPERM;
}
+static struct cdev *cdev_alloc(const char *name)
+{
+ struct cdev *new;
+
+ new = xzalloc(sizeof(*new));
+ new->name = xstrdup(name);
+
+ return new;
+}
+
int devfs_create(struct cdev *new)
{
struct cdev *cdev;
@@ -315,7 +358,7 @@ int devfs_create(struct cdev *new)
if (new->dev) {
list_add_tail(&new->devices_list, &new->dev->cdevs);
if (!new->device_node)
- new->device_node = new->dev->device_node;
+ new->device_node = new->dev->of_node;
}
return 0;
@@ -334,8 +377,7 @@ int devfs_create_link(struct cdev *cdev, const char *name)
*/
cdev = cdev_readlink(cdev);
- new = xzalloc(sizeof(*new));
- new->name = xstrdup(name);
+ new = cdev_alloc(name);
new->link = cdev;
if (cdev->partname) {
@@ -352,6 +394,7 @@ int devfs_create_link(struct cdev *cdev, const char *name)
}
INIT_LIST_HEAD(&new->links);
+ INIT_LIST_HEAD(&new->partitions);
list_add_tail(&new->list, &cdev_list);
list_add_tail(&new->link_entry, &cdev->links);
@@ -373,7 +416,10 @@ int devfs_remove(struct cdev *cdev)
list_for_each_entry_safe(c, tmp, &cdev->links, link_entry)
devfs_remove(c);
- if (cdev->master)
+ list_for_each_entry_safe(c, tmp, &cdev->partitions, partition_entry)
+ cdevfs_del_partition(c);
+
+ if (cdev_is_partition(cdev))
list_del(&cdev->partition_entry);
if (cdev->link)
@@ -382,6 +428,12 @@ int devfs_remove(struct cdev *cdev)
return 0;
}
+static bool region_identical(loff_t starta, loff_t lena,
+ loff_t startb, loff_t lenb)
+{
+ return starta == startb && lena == lenb;
+}
+
static bool region_overlap(loff_t starta, loff_t lena,
loff_t startb, loff_t lenb)
{
@@ -392,32 +444,66 @@ static bool region_overlap(loff_t starta, loff_t lena,
return 1;
}
-static int check_overlap(struct cdev *cdev, const char *name, loff_t offset, loff_t size)
+/**
+ * check_overlap() - check overlap with existing partitions
+ * @cdev: parent cdev
+ * @name: partition name for informational purposes on conflict
+ * @offset: offset of new partition to be added
+ * @size: size of new partition to be added
+ *
+ * Return: NULL if no overlapping partition found or overlapping
+ * partition if and only if it's identical in offset and size
+ * to an existing partition. Otherwise, PTR_ERR(-EINVAL).
+ */
+static struct cdev *check_overlap(struct cdev *cdev, const char *name, loff_t offset, loff_t size)
{
struct cdev *cpart;
+ loff_t cpart_offset;
+ int ret;
list_for_each_entry(cpart, &cdev->partitions, partition_entry) {
- if (region_overlap(cpart->offset, cpart->size,
- offset, size))
+ cpart_offset = cpart->offset;
+
+ /*
+ * An mtd partition is represented by a separate cdev and its
+ * cpart is relative to this one. So its .offset is 0 and we
+ * have to consult .master_offset to get its offset.
+ */
+ if (cpart->mtd)
+ cpart_offset = cpart->mtd->master_offset;
+
+ if (region_identical(cpart_offset, cpart->size, offset, size)) {
+ ret = 0;
+ goto identical;
+ }
+
+ if (region_overlap(cpart_offset, cpart->size, offset, size)) {
+ ret = -EINVAL;
goto conflict;
+ }
}
- return 0;
+ return NULL;
+identical:
conflict:
- pr_err("New partition %s (0x%08llx-0x%08llx) on %s "
- "overlaps with partition %s (0x%08llx-0x%08llx), not creating it\n",
- name, offset, offset + size - 1, cpart->name,
- cpart->name, cpart->offset, cpart->offset + cpart->size - 1);
+ __pr_printk(ret ? MSG_WARNING : MSG_DEBUG,
+ "New partition %s (0x%08llx-0x%08llx) on %s "
+ "%s with partition %s (0x%08llx-0x%08llx), not creating it\n",
+ name, offset, offset + size - 1, cdev->name,
+ ret ? "conflicts" : "identical",
+ cpart->name, cpart_offset, cpart_offset + cpart->size - 1);
- return -EINVAL;
+ return ret ? ERR_PTR(ret) : cpart;
}
static struct cdev *__devfs_add_partition(struct cdev *cdev,
const struct devfs_partition *partinfo, loff_t *end)
{
loff_t offset, size;
+ loff_t _end = end ? *end : 0;
static struct cdev *new;
+ struct cdev *overlap;
if (cdev_by_name(partinfo->name))
return ERR_PTR(-EEXIST);
@@ -426,7 +512,7 @@ static struct cdev *__devfs_add_partition(struct cdev *cdev,
offset = partinfo->offset;
else if (partinfo->offset == 0)
/* append to previous partition */
- offset = *end;
+ offset = _end;
else
/* relative to end of cdev */
offset = cdev->size + partinfo->offset;
@@ -436,20 +522,29 @@ static struct cdev *__devfs_add_partition(struct cdev *cdev,
else
size = cdev->size + partinfo->size - offset;
- if (offset >= 0 && offset < *end)
+ if (offset >= 0 && offset < _end)
pr_debug("partition %s not after previous partition\n",
partinfo->name);
- *end = offset + size;
+ _end = offset + size;
+ if (end)
+ *end = _end;
- if (offset < 0 || *end > cdev->size) {
+ if (offset < 0 || _end > cdev->size) {
pr_warn("partition %s not completely inside device %s\n",
partinfo->name, cdev->name);
return ERR_PTR(-EINVAL);
}
- if (check_overlap(cdev, partinfo->name, offset, size))
- return ERR_PTR(-EINVAL);
+ overlap = check_overlap(cdev, partinfo->name, offset, size);
+ if (overlap) {
+ if (!IS_ERR(overlap)) {
+ /* only fails with -EEXIST, which is fine */
+ (void)devfs_create_link(overlap, partinfo->name);
+ }
+
+ return overlap;
+ }
if (IS_ENABLED(CONFIG_MTD) && cdev->mtd) {
struct mtd_info *mtd;
@@ -463,8 +558,7 @@ static struct cdev *__devfs_add_partition(struct cdev *cdev,
return &mtd->cdev;
}
- new = xzalloc(sizeof(*new));
- new->name = strdup(partinfo->name);
+ new = cdev_alloc(partinfo->name);
if (!strncmp(cdev->name, partinfo->name, strlen(cdev->name)))
new->partname = xstrdup(partinfo->name + strlen(cdev->name) + 1);
@@ -485,11 +579,16 @@ static struct cdev *__devfs_add_partition(struct cdev *cdev,
return new;
}
+struct cdev *cdevfs_add_partition(struct cdev *cdev,
+ const struct devfs_partition *partinfo)
+{
+ return __devfs_add_partition(cdev, partinfo, NULL);
+}
+
struct cdev *devfs_add_partition(const char *devname, loff_t offset,
loff_t size, unsigned int flags, const char *name)
{
struct cdev *cdev;
- loff_t end = 0;
const struct devfs_partition partinfo = {
.offset = offset,
.size = size,
@@ -501,28 +600,21 @@ struct cdev *devfs_add_partition(const char *devname, loff_t offset,
if (!cdev)
return ERR_PTR(-ENOENT);
- return __devfs_add_partition(cdev, &partinfo, &end);
+ return cdevfs_add_partition(cdev, &partinfo);
}
-int devfs_del_partition(const char *name)
+int cdevfs_del_partition(struct cdev *cdev)
{
- struct cdev *cdev;
int ret;
- cdev = cdev_by_name(name);
- if (!cdev)
- return -ENOENT;
+ if (cdev->flags & DEVFS_PARTITION_FIXED)
+ return -EPERM;
if (IS_ENABLED(CONFIG_MTD) && cdev->mtd) {
ret = mtd_del_partition(cdev->mtd);
return ret;
}
- if (!cdev->master)
- return -EINVAL;
- if (cdev->flags & DEVFS_PARTITION_FIXED)
- return -EPERM;
-
ret = devfs_remove(cdev);
if (ret)
return ret;
@@ -534,6 +626,20 @@ int devfs_del_partition(const char *name)
return 0;
}
+int devfs_del_partition(const char *name)
+{
+ struct cdev *cdev;
+
+ cdev = cdev_by_name(name);
+ if (!cdev)
+ return -ENOENT;
+
+ if (!cdev_is_partition(cdev))
+ return -EINVAL;
+
+ return cdevfs_del_partition(cdev);
+}
+
int devfs_create_partitions(const char *devname,
const struct devfs_partition partinfo[])
{
@@ -596,6 +702,7 @@ static const struct cdev_operations loop_ops = {
struct cdev *cdev_create_loop(const char *path, ulong flags, loff_t offset)
{
+ char str[16];
struct cdev *new;
struct loop_priv *priv;
static int loopno;
@@ -609,10 +716,10 @@ struct cdev *cdev_create_loop(const char *path, ulong flags, loff_t offset)
return NULL;
}
- new = xzalloc(sizeof(*new));
+ snprintf(str, sizeof(str), "loop%u", loopno++);
+ new = cdev_alloc(str);
new->ops = &loop_ops;
- new->name = basprintf("loop%u", loopno++);
new->priv = priv;
ofs = lseek(priv->fd, 0, SEEK_END);
@@ -644,7 +751,7 @@ void cdev_remove_loop(struct cdev *cdev)
free(cdev);
}
-ssize_t mem_copy(struct device_d *dev, void *dst, const void *src,
+ssize_t mem_copy(struct device *dev, void *dst, const void *src,
resource_size_t count, resource_size_t offset,
unsigned long flags)
{
@@ -695,7 +802,7 @@ out:
ssize_t mem_read(struct cdev *cdev, void *buf, size_t count, loff_t offset,
unsigned long flags)
{
- struct device_d *dev = cdev->dev;
+ struct device *dev = cdev->dev;
if (!dev)
return -1;
@@ -708,7 +815,7 @@ EXPORT_SYMBOL(mem_read);
ssize_t mem_write(struct cdev *cdev, const void *buf, size_t count,
loff_t offset, unsigned long flags)
{
- struct device_d *dev = cdev->dev;
+ struct device *dev = cdev->dev;
if (!dev)
return -1;