diff mbox

ubiblock: Support UBI volume name or volume ID parameter passing

Message ID 1397141020-6363-1-git-send-email-ezequiel.garcia@free-electrons.com
State Superseded
Headers show

Commit Message

Ezequiel Garcia April 10, 2014, 2:43 p.m. UTC
In addition to the previous bevahior, this commit adds support for
ubiblock to identify a UBI volume based on the volume name or ID.

For example, the following command invocations are possible:

  $ ubiblock --create=/dev/ubi0_0   - create from UBI volume node
  $ ubiblock -r /dev/ubi0_0         - remove from UBI volume node

  $ ubiblock -c /dev/ubi0 -n 99     - create from UBI volume 99 from UBI device
  $ ubiblock -r /dev/ubi0 -N my_vol - remove from UBI volume named "my_vol" from UBI device

The implementation assume there's device node file named as "/dev/ubi${%d}_${%d}",
where the first integer is the UBI device number, and the second is the
UBI volume ID. This device is not strictly required to exist for a given volume,
so the implementation is a "best-effort".

For this reason, the open_volume_by_id is implemented here instead of adding
it to libubi.

Signed-off-by: Ezequiel Garcia <ezequiel.garcia@free-electrons.com>
---
 ubi-utils/ubiblock.c | 165 +++++++++++++++++++++++++++++++++++++++++----------
 1 file changed, 133 insertions(+), 32 deletions(-)

Comments

Artem Bityutskiy April 14, 2014, 10:42 a.m. UTC | #1
On Thu, 2014-04-10 at 11:43 -0300, Ezequiel Garcia wrote:
> +static int ubi_open_volume_by_id(libubi_t desc, const char *node, int vol_id, int mode)
>  {
> +       char file[256];
> +       struct ubi_dev_info dev_info;
>         int err, fd;
> -       libubi_t libubi;
>  
> -       err = parse_opt(argc, argv);
> +       err = ubi_get_dev_info(desc, node, &dev_info);
>         if (err)
> +               return errmsg("cannot get information about UBI device \"%s\"", node);
> +
> +       sprintf(file, "/dev/ubi%d_%d", dev_info.dev_num, vol_id);

Device node names may ve anything. Please, do not rely on the "/dev/ubi%
d_%d" naming scheme in the tool.

I think device node can be found out by scanning "/dev" and looking at
the device node major/minor numbers. This is more work of course.

However, libubi does exactly the same assumption about UBI volume names
as you do, so if you move this function to libubi, I think it will be
good enough. At least if someone wants to change this in the future,
then it will only require changing the library, but not its users.

> +       fd = open(file, mode);
> +       if (fd == -1)
> +               errmsg("Failed to open '%s' volume device", file);
> +       return fd;
> +}
> +
Ezequiel Garcia April 14, 2014, 6:45 p.m. UTC | #2
On Apr 14, Artem Bityutskiy wrote:
> On Thu, 2014-04-10 at 11:43 -0300, Ezequiel Garcia wrote:
> > +static int ubi_open_volume_by_id(libubi_t desc, const char *node, int vol_id, int mode)
> >  {
> > +       char file[256];
> > +       struct ubi_dev_info dev_info;
> >         int err, fd;
> > -       libubi_t libubi;
> >  
> > -       err = parse_opt(argc, argv);
> > +       err = ubi_get_dev_info(desc, node, &dev_info);
> >         if (err)
> > +               return errmsg("cannot get information about UBI device \"%s\"", node);
> > +
> > +       sprintf(file, "/dev/ubi%d_%d", dev_info.dev_num, vol_id);
> 
> Device node names may ve anything. Please, do not rely on the "/dev/ubi%
> d_%d" naming scheme in the tool.
> 

Yeah, I know this sucks.

> I think device node can be found out by scanning "/dev" and looking at
> the device node major/minor numbers. This is more work of course.
>

Ah, that's a good idea. Let me give it a try.

> However, libubi does exactly the same assumption about UBI volume names
> as you do, so if you move this function to libubi, I think it will be
> good enough. At least if someone wants to change this in the future,
> then it will only require changing the library, but not its users.
> 

OK, I will.
Ezequiel Garcia July 21, 2014, 8:53 p.m. UTC | #3
Sorry for the delay on this one.

On 14 Apr 01:42 PM, Artem Bityutskiy wrote:
> On Thu, 2014-04-10 at 11:43 -0300, Ezequiel Garcia wrote:
> > +static int ubi_open_volume_by_id(libubi_t desc, const char *node, int vol_id, int mode)
> >  {
> > +       char file[256];
> > +       struct ubi_dev_info dev_info;
> >         int err, fd;
> > -       libubi_t libubi;
> >  
> > -       err = parse_opt(argc, argv);
> > +       err = ubi_get_dev_info(desc, node, &dev_info);
> >         if (err)
> > +               return errmsg("cannot get information about UBI device \"%s\"", node);
> > +
> > +       sprintf(file, "/dev/ubi%d_%d", dev_info.dev_num, vol_id);
> 
> Device node names may ve anything. Please, do not rely on the "/dev/ubi%
> d_%d" naming scheme in the tool.
> 
> I think device node can be found out by scanning "/dev" and looking at
> the device node major/minor numbers. This is more work of course.
> 

Yeah, but the UBI major is allocated dynamically so we can't rely on that.
We would have to call an ioctl on each /dev device, which seems like a bit
heavy to me.

How about scanning out the sysfs and associate a name to the UBI device
and volume ID?

# ls -la /sys/class/ubi/
total 0
drwxr-xr-x  2 root root    0 Jul 21 20:23 .
drwxr-xr-x 37 root root    0 Jul 21 20:23 ..
lrwxrwxrwx  1 root root    0 Jul 21 20:23 ubi0 -> ../../devices/virtual/ubi/ubi0
lrwxrwxrwx  1 root root    0 Jul 21 20:23 ubi0_0 -> ../../devices/virtual/ubi/ubi0/ubi0_0
lrwxrwxrwx  1 root root    0 Jul 21 20:23 ubi0_1 -> ../../devices/virtual/ubi/ubi0/ubi0_1
lrwxrwxrwx  1 root root    0 Jul 21 20:23 ubi0_2 -> ../../devices/virtual/ubi/ubi0/ubi0_2
lrwxrwxrwx  1 root root    0 Jul 21 20:23 ubi0_3 -> ../../devices/virtual/ubi/ubi0/ubi0_3

Each /sys/class/ubiX_Y/name file can be related to the UBI device ${X},
and UBI volume ID ${Y}.

Based in this idea, we could make some ubifindvol tool that can find
an UBI volume ID from the UBI name.

> However, libubi does exactly the same assumption about UBI volume names
> as you do, so if you move this function to libubi, I think it will be
> good enough. At least if someone wants to change this in the future,
> then it will only require changing the library, but not its users.
>
Artem Bityutskiy July 28, 2014, 4:31 p.m. UTC | #4
On Mon, 2014-07-21 at 17:53 -0300, Ezequiel Garcia wrote:
> Sorry for the delay on this one.
> 
> On 14 Apr 01:42 PM, Artem Bityutskiy wrote:
> > On Thu, 2014-04-10 at 11:43 -0300, Ezequiel Garcia wrote:
> > > +static int ubi_open_volume_by_id(libubi_t desc, const char *node, int vol_id, int mode)
> > >  {
> > > +       char file[256];
> > > +       struct ubi_dev_info dev_info;
> > >         int err, fd;
> > > -       libubi_t libubi;
> > >  
> > > -       err = parse_opt(argc, argv);
> > > +       err = ubi_get_dev_info(desc, node, &dev_info);
> > >         if (err)
> > > +               return errmsg("cannot get information about UBI device \"%s\"", node);
> > > +
> > > +       sprintf(file, "/dev/ubi%d_%d", dev_info.dev_num, vol_id);
> > 
> > Device node names may ve anything. Please, do not rely on the "/dev/ubi%
> > d_%d" naming scheme in the tool.
> > 
> > I think device node can be found out by scanning "/dev" and looking at
> > the device node major/minor numbers. This is more work of course.
> > 
> 
> Yeah, but the UBI major is allocated dynamically so we can't rely on that.
> We would have to call an ioctl on each /dev device, which seems like a bit
> heavy to me.
> 
> How about scanning out the sysfs and associate a name to the UBI device
> and volume ID?

What I meant is that you have these:

/sys/class/ubi/ubiX/dev

and these

/sys/class/ubi/ubiX/ubiX_Y/dev

containing the "major:minor" pairs.

For for any X:Y you can find major:minor. Then you scan "/dev" and find
the device nodes, if they exist. If they do not exist, you may ask the
user to create a device node with these major:minor in your error
message.
Ezequiel Garcia July 30, 2014, 2:22 p.m. UTC | #5
On 28 Jul 07:31 PM, Artem Bityutskiy wrote:
> On Mon, 2014-07-21 at 17:53 -0300, Ezequiel Garcia wrote:
> > Sorry for the delay on this one.
> > 
> > On 14 Apr 01:42 PM, Artem Bityutskiy wrote:
> > > On Thu, 2014-04-10 at 11:43 -0300, Ezequiel Garcia wrote:
> > > > +static int ubi_open_volume_by_id(libubi_t desc, const char *node, int vol_id, int mode)
> > > >  {
> > > > +       char file[256];
> > > > +       struct ubi_dev_info dev_info;
> > > >         int err, fd;
> > > > -       libubi_t libubi;
> > > >  
> > > > -       err = parse_opt(argc, argv);
> > > > +       err = ubi_get_dev_info(desc, node, &dev_info);
> > > >         if (err)
> > > > +               return errmsg("cannot get information about UBI device \"%s\"", node);
> > > > +
> > > > +       sprintf(file, "/dev/ubi%d_%d", dev_info.dev_num, vol_id);
> > > 
> > > Device node names may ve anything. Please, do not rely on the "/dev/ubi%
> > > d_%d" naming scheme in the tool.
> > > 
> > > I think device node can be found out by scanning "/dev" and looking at
> > > the device node major/minor numbers. This is more work of course.
> > > 
> > 
> > Yeah, but the UBI major is allocated dynamically so we can't rely on that.
> > We would have to call an ioctl on each /dev device, which seems like a bit
> > heavy to me.
> > 
> > How about scanning out the sysfs and associate a name to the UBI device
> > and volume ID?
> 
> What I meant is that you have these:
> 
> /sys/class/ubi/ubiX/dev
> 
> and these
> 
> /sys/class/ubi/ubiX/ubiX_Y/dev
> 
> containing the "major:minor" pairs.
> 
> For for any X:Y you can find major:minor. Then you scan "/dev" and find
> the device nodes, if they exist. If they do not exist, you may ask the
> user to create a device node with these major:minor in your error
> message.
> 

Right. That sounds a nice solution. I'll implement something upstreamable
as time permits.

Thanks!
diff mbox

Patch

diff --git a/ubi-utils/ubiblock.c b/ubi-utils/ubiblock.c
index 1e12be8..a599576 100644
--- a/ubi-utils/ubiblock.c
+++ b/ubi-utils/ubiblock.c
@@ -34,49 +34,118 @@ 
 #include <libubi.h>
 #include "common.h"
 
+enum operation {
+	UBIBLOCK_NONE,
+	UBIBLOCK_CREATE,
+	UBIBLOCK_REMOVE,
+};
+
 struct args {
+	int vol_id;
 	const char *node;
-	int create;
+	const char *name;
+	enum operation op;
 };
 
-static struct args args;
+static struct args args = {
+	.op = UBIBLOCK_NONE,
+	.vol_id = -1,
+};
 
 static const char doc[] = PROGRAM_NAME " version " VERSION
 			 " - a tool to create/remove block device interface from UBI volumes.";
 
 static const char optionsstr[] =
-"-c, --create               create block on top of a volume\n"
-"-r, --remove               remove block from volume\n"
-"-h, --help                 print help message\n"
-"-V, --version              print program version";
+"-c, --create=<UBI device/volume node>	create block on top of a volume\n"
+"-r, --remove=<UBI device/volume node>	remove block from volume\n"
+"-n, --vol_id=<volume id>		volume ID\n"
+"-N, --name=<volume name>		volume name\n"
+"-h, --help				print help message\n"
+"-V, --version				print program version";
 
 static const char usage[] =
-"Usage: " PROGRAM_NAME " [-c,-r] <UBI volume node file name>\n"
-"Example: " PROGRAM_NAME " --create /dev/ubi0_0";
+"Usage: " PROGRAM_NAME " [-c,-r] <UBI device or UBI volume node file name>\n"
+"         [-n <volume id>] [--vol_id=<volume id>]\n"
+"         [-N <volume name>] [--name=<volume name>] [-h] [--help]\n\n"
+"Example: " PROGRAM_NAME " --create=/dev/ubi0_0   - create from UBI volume node\n"
+"         " PROGRAM_NAME " -c /dev/ubi0 -n 1      - create from UBI volume 1 from UBI device\n"
+"         " PROGRAM_NAME " -r /dev/ubi0 -N my_vol - remove from UBI volume named \"my_vol\" from UBI device";
 
 static const struct option long_options[] = {
 	{ .name = "create",   .has_arg = 1, .flag = NULL, .val = 'c' },
 	{ .name = "remove",   .has_arg = 1, .flag = NULL, .val = 'r' },
+	{ .name = "vol_id",   .has_arg = 1, .flag = NULL, .val = 'n' },
+	{ .name = "name",     .has_arg = 1, .flag = NULL, .val = 'N' },
 	{ .name = "help",     .has_arg = 0, .flag = NULL, .val = 'h' },
 	{ .name = "version",  .has_arg = 0, .flag = NULL, .val = 'V' },
 	{ NULL, 0, NULL, 0}
 };
 
-static int parse_opt(int argc, char * const argv[])
+static int param_sanity_check(libubi_t libubi)
+{
+	int err;
+
+	if (args.op == UBIBLOCK_NONE) {
+		errmsg("please, specify a creation or removal operation");
+		return -1;
+	}
+
+	err = ubi_probe_node(libubi, args.node);
+	if (err == 2) {
+		/* UBI volume node, don't need volume ID or name */
+		return 0;
+	} else if (err == 1) {
+		/* UBI device node, we need volume ID or name */
+		;
+	} else if (err < 0) {
+		if (errno == ENODEV)
+			errmsg("\"%s\" is not an UBI device node", args.node);
+		else
+			errmsg("error while probing \"%s\"", args.node);
+		return -1;
+	}
+
+	if (args.vol_id == -1 && !args.name) {
+		errmsg("please, specify either volume ID or volume name");
+		return -1;
+	}
+
+	if (args.vol_id != -1 && args.name) {
+		errmsg("please, specify either volume ID or volume name, not both");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int parse_opt(libubi_t libubi, int argc, char * const argv[])
 {
 	while (1) {
-		int key;
+		int key, error = 0;
 
-		key = getopt_long(argc, argv, "c:r:h?V", long_options, NULL);
+		key = getopt_long(argc, argv, "n:N:c:r:h?V", long_options, NULL);
 		if (key == -1)
 			break;
 
 		switch (key) {
 		case 'c':
-			args.create = 1;
+			args.op = UBIBLOCK_CREATE;
+			args.node = optarg;
+			break;
 		case 'r':
+			args.op = UBIBLOCK_REMOVE;
 			args.node = optarg;
 			break;
+		case 'n':
+			args.vol_id = simple_strtoul(optarg, &error);
+			if (error || args.vol_id < 0) {
+				errmsg("bad volume ID: " "\"%s\"", optarg);
+				return -1;
+			}
+			break;
+		case 'N':
+			args.name = optarg;
+			break;
 		case 'h':
 		case '?':
 			printf("%s\n\n", doc);
@@ -94,20 +163,52 @@  static int parse_opt(int argc, char * const argv[])
 		}
 	}
 
-	if (!args.node)
-		return errmsg("invalid arguments (use -h for help)");
-
+	if (param_sanity_check(libubi))
+		return -1;
 	return 0;
 }
 
-int main(int argc, char * const argv[])
+static int ubi_open_volume_by_id(libubi_t desc, const char *node, int vol_id, int mode)
 {
+	char file[256];
+	struct ubi_dev_info dev_info;
 	int err, fd;
-	libubi_t libubi;
 
-	err = parse_opt(argc, argv);
+	err = ubi_get_dev_info(desc, node, &dev_info);
 	if (err)
+		return errmsg("cannot get information about UBI device \"%s\"", node);
+
+	sprintf(file, "/dev/ubi%d_%d", dev_info.dev_num, vol_id);
+	fd = open(file, mode);
+	if (fd == -1)
+		errmsg("Failed to open '%s' volume device", file);
+	return fd;
+}
+
+static int ubi_open_volume_by_name(libubi_t desc, const char *node, const char *name, int mode)
+{
+	struct ubi_dev_info dev_info;
+	struct ubi_vol_info vol_info;
+	int err;
+
+	err = ubi_get_dev_info(desc, node, &dev_info);
+	if (err) {
+		errmsg("cannot get information about UBI device \"%s\"", node);
+		return -1;
+	}
+
+	err = ubi_get_vol_info1_nm(desc, dev_info.dev_num, name, &vol_info);
+	if (err) {
+		errmsg("cannot find UBI volume \"%s\"", name);
 		return -1;
+	}
+	return ubi_open_volume_by_id(desc, node, vol_info.vol_id, mode);
+}
+
+int main(int argc, char * const argv[])
+{
+	int err, fd;
+	libubi_t libubi;
 
 	libubi = libubi_open();
 	if (!libubi) {
@@ -116,26 +217,26 @@  int main(int argc, char * const argv[])
 		return sys_errmsg("cannot open libubi");
 	}
 
-	err = ubi_probe_node(libubi, args.node);
-	if (err == 1) {
-		errmsg("\"%s\" is an UBI device node, not an UBI volume node",
-		       args.node);
-		goto out_libubi;
-	} else if (err < 0) {
-		if (errno == ENODEV)
-			errmsg("\"%s\" is not an UBI volume node", args.node);
-		else
-			sys_errmsg("error while probing \"%s\"", args.node);
-		goto out_libubi;
+	err = parse_opt(libubi, argc, argv);
+	if (err)
+		return -1;
+
+	if (args.name) {
+		fd = ubi_open_volume_by_name(libubi, args.node, args.name, O_RDWR);
+	} else if (args.vol_id != -1) {
+		fd = ubi_open_volume_by_id(libubi, args.node, args.vol_id, O_RDWR);
+	} else {
+		fd = open(args.node, O_RDWR);
+		if (fd == -1)
+			errmsg("Failed to open '%s' volume device", args.node);
 	}
 
-	fd = open(args.node, O_RDWR);
 	if (fd == -1) {
-		sys_errmsg("cannot open UBI volume \"%s\"", args.node);
+		sys_errmsg("cannot open UBI volume device");
 		goto out_libubi;
 	}
 
-	if (args.create) {
+	if (args.op == UBIBLOCK_CREATE) {
 		err = ubi_vol_block_create(fd);
 		if (err) {
 			if (errno == ENOSYS)