summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--commands/ubi.c116
-rw-r--r--drivers/mtd/ubi/barebox.c199
-rw-r--r--drivers/mtd/ubi/vmt.c3
-rw-r--r--include/linux/mtd/ubi.h4
-rw-r--r--include/mtd/ubi-user.h2
5 files changed, 302 insertions, 22 deletions
diff --git a/commands/ubi.c b/commands/ubi.c
index 26b521f374..4c445d2216 100644
--- a/commands/ubi.c
+++ b/commands/ubi.c
@@ -9,6 +9,7 @@
#include <linux/kernel.h>
#include <linux/stat.h>
#include <linux/mtd/mtd-abi.h>
+#include <linux/mtd/ubi.h>
#include <mtd/ubi-user.h>
#include <mtd/ubi-media.h>
@@ -104,6 +105,7 @@ static int do_ubimkvol(int argc, char *argv[])
struct ubi_mkvol_req req;
int fd, ret;
uint64_t size;
+ uint32_t ubinum;
req.vol_type = UBI_DYNAMIC_VOLUME;
@@ -140,9 +142,14 @@ static int do_ubimkvol(int argc, char *argv[])
return 1;
}
- ret = ioctl(fd, UBI_IOCMKVOL, &req);
- if (ret)
- printf("failed to create: %s\n", strerror(-ret));
+ ret = ioctl(fd, UBI_IOCGETUBINUM, &ubinum);
+ if (ret) {
+ printf("failed to get ubinum: %s\n", strerror(-ret));
+ goto err;
+ }
+
+ ret = ubi_api_create_volume(ubinum, &req);
+err:
close(fd);
return ret ? 1 : 0;
@@ -272,24 +279,38 @@ BAREBOX_CMD_END
static int do_ubirmvol(int argc, char *argv[])
{
- struct ubi_mkvol_req req;
+ struct ubi_volume_desc *desc;
+ uint32_t ubinum;
int fd, ret;
if (argc != 3)
return COMMAND_ERROR_USAGE;
- strncpy(req.name, argv[2], UBI_VOL_NAME_MAX);
- req.name[UBI_VOL_NAME_MAX] = 0;
-
fd = open(argv[1], O_WRONLY);
if (fd < 0) {
perror("open");
return 1;
}
- ret = ioctl(fd, UBI_IOCRMVOL, &req);
+ ret = ioctl(fd, UBI_IOCGETUBINUM, &ubinum);
+ if (ret) {
+ printf("failed to get ubinum: %s\n", strerror(-ret));
+ goto err;
+ }
+
+ desc = ubi_open_volume_nm(ubinum, argv[2], UBI_EXCLUSIVE);
+ if (IS_ERR(desc)) {
+ ret = PTR_ERR(desc);
+ printf("failed to open volume %s: %s\n", argv[2], strerror(-ret));
+ goto err;
+ }
+
+ ret = ubi_api_remove_volume(desc, 0);
if (ret)
- printf("failed to delete: %s\n", strerror(-ret));
+ printf("failed to remove volume %s: %s\n", argv[2], strerror(-ret));
+
+ ubi_close_volume(desc);
+err:
close(fd);
return ret ? 1 : 0;
@@ -306,3 +327,80 @@ BAREBOX_CMD_START(ubirmvol)
BAREBOX_CMD_GROUP(CMD_GRP_PART)
BAREBOX_CMD_HELP(cmd_ubirmvol_help)
BAREBOX_CMD_END
+
+static int get_vol_id(u32 ubi_num, const char *name)
+{
+ struct ubi_volume_desc *desc;
+ struct ubi_volume_info vi;
+
+ desc = ubi_open_volume_nm(ubi_num, name, UBI_READONLY);
+ if(IS_ERR(desc))
+ return PTR_ERR(desc);
+
+ ubi_get_volume_info(desc, &vi);
+ ubi_close_volume(desc);
+
+ return vi.vol_id;
+};
+
+static int do_ubirename(int argc, char *argv[])
+{
+ struct ubi_rnvol_req req;
+ u32 ubi_num;
+ int i, j, fd, ret;
+
+ if ((argc < 4) || (argc % 2))
+ return COMMAND_ERROR_USAGE;
+
+ req.count = (argc / 2) - 1;
+ if (req.count > UBI_MAX_RNVOL) {
+ printf("too many volume renames. (max: %u)\n", UBI_MAX_RNVOL);
+ return COMMAND_ERROR_USAGE;
+ }
+
+ fd = open(argv[1], O_WRONLY);
+ if (fd < 0) {
+ perror("unable to open the UBI device");
+ return 1;
+ }
+
+ ret = ioctl(fd, UBI_IOCGETUBINUM, &ubi_num);
+
+ close(fd);
+
+ if (ret) {
+ printf("failed to get ubi num for %s: %s\n", argv[1], strerror(-ret));
+ return 1;
+ }
+
+ for (i = 2, j = 0; i < argc; ++j, i += 2) {
+ req.ents[j].vol_id = get_vol_id(ubi_num, argv[i]);
+
+ if (req.ents[j].vol_id < 0) {
+ printf("Volume '%s' does not exist on %s\n", argv[i], argv[1]);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ strncpy(req.ents[j].name, argv[i + 1], UBI_MAX_VOLUME_NAME);
+ req.ents[j].name_len = strlen(req.ents[j].name);
+ }
+
+ ret = ubi_api_rename_volumes(ubi_num, &req);
+ if (ret)
+ printf("failed to rename: %s", strerror(-ret));
+err:
+ return ret ? 1 : 0;
+};
+
+BAREBOX_CMD_HELP_START(ubirename)
+BAREBOX_CMD_HELP_TEXT("Rename UBI volume(s) from UBIDEV")
+BAREBOX_CMD_HELP_END
+
+BAREBOX_CMD_START(ubirename)
+ .cmd = do_ubirename,
+ BAREBOX_CMD_DESC("rename UBI volume(s)")
+ BAREBOX_CMD_OPTS("UBIDEV OLD_NAME NEW_NAME [OLD_NAME NEW_NAME ...]")
+ BAREBOX_CMD_GROUP(CMD_GRP_PART)
+ BAREBOX_CMD_HELP(cmd_ubirename_help)
+BAREBOX_CMD_END
diff --git a/drivers/mtd/ubi/barebox.c b/drivers/mtd/ubi/barebox.c
index fc60aaeec5..329cf4564a 100644
--- a/drivers/mtd/ubi/barebox.c
+++ b/drivers/mtd/ubi/barebox.c
@@ -257,25 +257,198 @@ void ubi_volume_cdev_remove(struct ubi_volume *vol)
kfree(priv);
}
+int ubi_api_create_volume(int ubi_num, struct ubi_mkvol_req *req)
+{
+ struct ubi_device *ubi;
+ int ret;
+
+ ubi = ubi_get_device(ubi_num);
+ if (!ubi)
+ return -ENODEV;
+
+ if (!req->bytes)
+ req->bytes = (__s64)ubi->avail_pebs * ubi->leb_size;
+ ret = ubi_create_volume(ubi, req);
+
+ ubi_put_device(ubi);
+
+ return ret;
+}
+
+int ubi_api_remove_volume(struct ubi_volume_desc *desc, int no_vtbl)
+{
+ return ubi_remove_volume(desc, no_vtbl);
+}
+
+int ubi_api_rename_volumes(int ubi_num, struct ubi_rnvol_req *req)
+{
+ int i, n, err;
+ struct list_head rename_list;
+ struct ubi_rename_entry *re, *re1;
+ struct ubi_device *ubi;
+
+ if (req->count < 0 || req->count > UBI_MAX_RNVOL)
+ return -EINVAL;
+
+ if (req->count == 0)
+ return 0;
+
+ ubi = ubi_get_device(ubi_num);
+ if (!ubi)
+ return -ENODEV;
+
+ /* Validate volume IDs and names in the request */
+ for (i = 0; i < req->count; i++) {
+ if (req->ents[i].vol_id < 0 ||
+ req->ents[i].vol_id >= ubi->vtbl_slots)
+ return -EINVAL;
+ if (req->ents[i].name_len < 0)
+ return -EINVAL;
+ if (req->ents[i].name_len > UBI_VOL_NAME_MAX)
+ return -ENAMETOOLONG;
+ req->ents[i].name[req->ents[i].name_len] = '\0';
+ n = strlen(req->ents[i].name);
+ if (n != req->ents[i].name_len)
+ return -EINVAL;
+ }
+
+ /* Make sure volume IDs and names are unique */
+ for (i = 0; i < req->count - 1; i++) {
+ for (n = i + 1; n < req->count; n++) {
+ if (req->ents[i].vol_id == req->ents[n].vol_id) {
+ ubi_err(ubi, "duplicated volume id %d",
+ req->ents[i].vol_id);
+ return -EINVAL;
+ }
+ if (!strcmp(req->ents[i].name, req->ents[n].name)) {
+ ubi_err(ubi, "duplicated volume name \"%s\"",
+ req->ents[i].name);
+ return -EINVAL;
+ }
+ }
+ }
+
+ /* Create the re-name list */
+ INIT_LIST_HEAD(&rename_list);
+ for (i = 0; i < req->count; i++) {
+ int vol_id = req->ents[i].vol_id;
+ int name_len = req->ents[i].name_len;
+ const char *name = req->ents[i].name;
+
+ re = kzalloc(sizeof(struct ubi_rename_entry), GFP_KERNEL);
+ if (!re) {
+ err = -ENOMEM;
+ goto out_free;
+ }
+
+ re->desc = ubi_open_volume(ubi->ubi_num, vol_id, UBI_READONLY);
+ if (IS_ERR(re->desc)) {
+ err = PTR_ERR(re->desc);
+ ubi_err(ubi, "cannot open volume %d, error %d",
+ vol_id, err);
+ kfree(re);
+ goto out_free;
+ }
+
+ /* Skip this re-naming if the name does not really change */
+ if (re->desc->vol->name_len == name_len &&
+ !memcmp(re->desc->vol->name, name, name_len)) {
+ ubi_close_volume(re->desc);
+ kfree(re);
+ continue;
+ }
+
+ re->new_name_len = name_len;
+ memcpy(re->new_name, name, name_len);
+ list_add_tail(&re->list, &rename_list);
+ dbg_gen("will rename volume %d from \"%s\" to \"%s\"",
+ vol_id, re->desc->vol->name, name);
+ }
+
+ if (list_empty(&rename_list))
+ return 0;
+
+ /* Find out the volumes which have to be removed */
+ list_for_each_entry(re, &rename_list, list) {
+ struct ubi_volume_desc *desc;
+ int no_remove_needed = 0;
+
+ /*
+ * Volume @re->vol_id is going to be re-named to
+ * @re->new_name, while its current name is @name. If a volume
+ * with name @re->new_name currently exists, it has to be
+ * removed, unless it is also re-named in the request (@req).
+ */
+ list_for_each_entry(re1, &rename_list, list) {
+ if (re->new_name_len == re1->desc->vol->name_len &&
+ !memcmp(re->new_name, re1->desc->vol->name,
+ re1->desc->vol->name_len)) {
+ no_remove_needed = 1;
+ break;
+ }
+ }
+
+ if (no_remove_needed)
+ continue;
+
+ /*
+ * It seems we need to remove volume with name @re->new_name,
+ * if it exists.
+ */
+ desc = ubi_open_volume_nm(ubi->ubi_num, re->new_name,
+ UBI_EXCLUSIVE);
+ if (IS_ERR(desc)) {
+ err = PTR_ERR(desc);
+ if (err == -ENODEV)
+ /* Re-naming into a non-existing volume name */
+ continue;
+
+ /* The volume exists but busy, or an error occurred */
+ ubi_err(ubi, "cannot open volume \"%s\", error %d",
+ re->new_name, err);
+ goto out_free;
+ }
+
+ re1 = kzalloc(sizeof(struct ubi_rename_entry), GFP_KERNEL);
+ if (!re1) {
+ err = -ENOMEM;
+ ubi_close_volume(desc);
+ goto out_free;
+ }
+
+ re1->remove = 1;
+ re1->desc = desc;
+ list_add(&re1->list, &rename_list);
+ dbg_gen("will remove volume %d, name \"%s\"",
+ re1->desc->vol->vol_id, re1->desc->vol->name);
+ }
+
+ mutex_lock(&ubi->device_mutex);
+ err = ubi_rename_volumes(ubi, &rename_list);
+ mutex_unlock(&ubi->device_mutex);
+
+out_free:
+ list_for_each_entry_safe(re, re1, &rename_list, list) {
+ ubi_close_volume(re->desc);
+ list_del(&re->list);
+ kfree(re);
+ }
+ return err;
+}
+
static int ubi_cdev_ioctl(struct cdev *cdev, int cmd, void *buf)
{
- struct ubi_volume_desc *desc;
struct ubi_device *ubi = cdev->priv;
- struct ubi_mkvol_req *req = buf;
switch (cmd) {
- case UBI_IOCRMVOL:
- desc = ubi_open_volume_nm(ubi->ubi_num, req->name,
- UBI_EXCLUSIVE);
- if (IS_ERR(desc))
- return PTR_ERR(desc);
- ubi_remove_volume(desc, 0);
- ubi_close_volume(desc);
+ /*
+ * Only supported ioctl is a barebox specific one to get the ubi_num
+ * from the file descriptor. The rest is implemented as function calls
+ * directly.
+ */
+ case UBI_IOCGETUBINUM:
+ *(u32 *)buf = ubi->ubi_num;
break;
- case UBI_IOCMKVOL:
- if (!req->bytes)
- req->bytes = (__s64)ubi->avail_pebs * ubi->leb_size;
- return ubi_create_volume(ubi, req);
};
return 0;
diff --git a/drivers/mtd/ubi/vmt.c b/drivers/mtd/ubi/vmt.c
index 41b814cf90..ed043646b8 100644
--- a/drivers/mtd/ubi/vmt.c
+++ b/drivers/mtd/ubi/vmt.c
@@ -411,6 +411,9 @@ int ubi_rename_volumes(struct ubi_device *ubi, struct list_head *rename_list)
vol->name_len = re->new_name_len;
memcpy(vol->name, re->new_name, re->new_name_len + 1);
+ free(vol->cdev.name);
+ vol->cdev.name = basprintf("%s.%s", ubi->cdev.name, vol->name);
+ vol->cdev.size = vol->used_bytes;
ubi_volume_notify(ubi, vol, UBI_VOLUME_RENAMED);
}
}
diff --git a/include/linux/mtd/ubi.h b/include/linux/mtd/ubi.h
index 0614681d73..c215ff20f7 100644
--- a/include/linux/mtd/ubi.h
+++ b/include/linux/mtd/ubi.h
@@ -218,6 +218,10 @@ int ubi_is_mapped(struct ubi_volume_desc *desc, int lnum);
int ubi_sync(int ubi_num);
int ubi_flush(int ubi_num, int vol_id, int lnum);
+int ubi_api_create_volume(int ubi_num, struct ubi_mkvol_req *req);
+int ubi_api_remove_volume(struct ubi_volume_desc *desc, int no_vtbl);
+int ubi_api_rename_volumes(int ubi_num, struct ubi_rnvol_req *req);
+
/*
* This function is the same as the 'ubi_leb_read()' function, but it does not
* provide the checking capability.
diff --git a/include/mtd/ubi-user.h b/include/mtd/ubi-user.h
index 8c02f96e4c..9425533d19 100644
--- a/include/mtd/ubi-user.h
+++ b/include/mtd/ubi-user.h
@@ -161,6 +161,8 @@
/* Re-name volumes */
#define UBI_IOCRNVOL _IOW(UBI_IOC_MAGIC, 3, struct ubi_rnvol_req)
+#define UBI_IOCGETUBINUM _IOR(UBI_IOC_MAGIC, 32, __u32)
+
/* ioctl commands of the UBI control character device */
#define UBI_CTRL_IOC_MAGIC 'o'