2008-11-17 07:58:31

by Tomas M

[permalink] [raw]
Subject: [PATCH] loop file resizable

The following patch against loop.c adds ability to 'resize' the loop device on the fly.
This may be practically very useful.

One practical application is a loop file with XFS filesystem, already mounted:
You can easily enlarge the file (append some bytes) and then call
ioctl(fd, LOOP_SET_CAPACITY, new);
The loop driver will learn about the new size and you can use xfs_growfs
later on, which will allow you to use full capacity of the loop
file without the need to unmount.

Please include it in mainline kernel.

Hope you like it.

Signed-off-by: Junjiro Okajima <[email protected]>
Signed-off-by: Tomas Matejicek <[email protected]>

Tomas M
slax.org


Index: linux-2.6.27/drivers/block/loop.c
===================================================================
retrieving revision 1.1
diff -u -p -r1.1 loop.c
--- linux-2.6.27/drivers/block/loop.c 7 Nov 2008 13:07:06 -0000 1.1
+++ linux-2.6.27/drivers/block/loop.c 8 Nov 2008 03:29:47 -0000
@@ -1142,6 +1142,8 @@ static int lo_ioctl(struct inode * inode
{
struct loop_device *lo = inode->i_bdev->bd_disk->private_data;
int err;
+ sector_t sec;
+ loff_t sz;

mutex_lock(&lo->lo_ctl_mutex);
switch (cmd) {
@@ -1166,6 +1168,16 @@ static int lo_ioctl(struct inode * inode
case LOOP_GET_STATUS64:
err = loop_get_status64(lo, (struct loop_info64 __user *) arg);
break;
+ case LOOP_SET_CAPACITY:
+ err = figure_loop_size(lo);
+ if (!err) {
+ sec = get_capacity(lo->lo_disk);
+ sz = sec << 9;
+ mutex_lock(&inode->i_bdev->bd_mutex);
+ bd_set_size(inode->i_bdev, sz);
+ mutex_unlock(&inode->i_bdev->bd_mutex);
+ }
+ break;
default:
err = lo->ioctl ? lo->ioctl(lo, cmd, arg) : -EINVAL;
}
@@ -1311,6 +1323,7 @@ static long lo_compat_ioctl(struct file
lo, (struct compat_loop_info __user *) arg);
mutex_unlock(&lo->lo_ctl_mutex);
break;
+ case LOOP_SET_CAPACITY:
case LOOP_CLR_FD:
case LOOP_GET_STATUS64:
case LOOP_SET_STATUS64:
Index: linux-2.6.27/include/linux/loop.h
===================================================================
retrieving revision 1.1
diff -u -p -r1.1 loop.h
--- linux-2.6.27/include/linux/loop.h 7 Nov 2008 13:05:58 -0000 1.1
+++ linux-2.6.27/include/linux/loop.h 8 Nov 2008 03:22:36 -0000
@@ -160,5 +160,6 @@ int loop_unregister_transfer(int number)
#define LOOP_SET_STATUS64 0x4C04
#define LOOP_GET_STATUS64 0x4C05
#define LOOP_CHANGE_FD 0x4C06
+#define LOOP_SET_CAPACITY 0x4C07

#endif


2008-11-19 00:42:49

by Andrew Morton

[permalink] [raw]
Subject: Re: [PATCH] loop file resizable

On Mon, 17 Nov 2008 08:44:28 +0100
Tomas M <[email protected]> wrote:

> The following patch against loop.c adds ability to 'resize' the loop device on the fly.
> This may be practically very useful.
>
> One practical application is a loop file with XFS filesystem, already mounted:
> You can easily enlarge the file (append some bytes) and then call
> ioctl(fd, LOOP_SET_CAPACITY, new);
> The loop driver will learn about the new size and you can use xfs_growfs
> later on, which will allow you to use full capacity of the loop
> file without the need to unmount.
>

Various people have tried this before, if I recall correctly. One was
Akinobu Mita (cc'ed here).

I don't recall why that patch never was merged?

>
>
> Index: linux-2.6.27/drivers/block/loop.c
> ===================================================================
> retrieving revision 1.1
> diff -u -p -r1.1 loop.c
> --- linux-2.6.27/drivers/block/loop.c 7 Nov 2008 13:07:06 -0000 1.1
> +++ linux-2.6.27/drivers/block/loop.c 8 Nov 2008 03:29:47 -0000
> @@ -1142,6 +1142,8 @@ static int lo_ioctl(struct inode * inode
> {
> struct loop_device *lo = inode->i_bdev->bd_disk->private_data;
> int err;
> + sector_t sec;
> + loff_t sz;
>
> mutex_lock(&lo->lo_ctl_mutex);
> switch (cmd) {
> @@ -1166,6 +1168,16 @@ static int lo_ioctl(struct inode * inode
> case LOOP_GET_STATUS64:
> err = loop_get_status64(lo, (struct loop_info64 __user *) arg);
> break;
> + case LOOP_SET_CAPACITY:
> + err = figure_loop_size(lo);
> + if (!err) {

It would be neater to declare `sec' and `sz' here.

> + sec = get_capacity(lo->lo_disk);
> + sz = sec << 9;
> + mutex_lock(&inode->i_bdev->bd_mutex);
> + bd_set_size(inode->i_bdev, sz);
> + mutex_unlock(&inode->i_bdev->bd_mutex);
> + }
> + break;
> default:
> err = lo->ioctl ? lo->ioctl(lo, cmd, arg) : -EINVAL;
> }
> @@ -1311,6 +1323,7 @@ static long lo_compat_ioctl(struct file
> lo, (struct compat_loop_info __user *) arg);
> mutex_unlock(&lo->lo_ctl_mutex);
> break;
> + case LOOP_SET_CAPACITY:
> case LOOP_CLR_FD:
> case LOOP_GET_STATUS64:
> case LOOP_SET_STATUS64:
> Index: linux-2.6.27/include/linux/loop.h
> ===================================================================
> retrieving revision 1.1
> diff -u -p -r1.1 loop.h
> --- linux-2.6.27/include/linux/loop.h 7 Nov 2008 13:05:58 -0000 1.1
> +++ linux-2.6.27/include/linux/loop.h 8 Nov 2008 03:22:36 -0000
> @@ -160,5 +160,6 @@ int loop_unregister_transfer(int number)
> #define LOOP_SET_STATUS64 0x4C04
> #define LOOP_GET_STATUS64 0x4C05
> #define LOOP_CHANGE_FD 0x4C06
> +#define LOOP_SET_CAPACITY 0x4C07

2008-11-19 02:07:24

by Akinobu Mita

[permalink] [raw]
Subject: Re: [PATCH] loop file resizable

2008/11/19 Andrew Morton <[email protected]>:
> On Mon, 17 Nov 2008 08:44:28 +0100
> Tomas M <[email protected]> wrote:
>
>> The following patch against loop.c adds ability to 'resize' the loop device on the fly.
>> This may be practically very useful.
>>
>> One practical application is a loop file with XFS filesystem, already mounted:
>> You can easily enlarge the file (append some bytes) and then call
>> ioctl(fd, LOOP_SET_CAPACITY, new);
>> The loop driver will learn about the new size and you can use xfs_growfs
>> later on, which will allow you to use full capacity of the loop
>> file without the need to unmount.
>>
>
> Various people have tried this before, if I recall correctly. One was
> Akinobu Mita (cc'ed here).
>
> I don't recall why that patch never was merged?

Yes, I did.
http://lkml.org/lkml/2006/5/23/26

There wasn't outstanding problem in that patch.
But I didn't send updated patch. Because I was just lazy.

2008-11-19 03:51:23

by J. R. Okajima

[permalink] [raw]
Subject: Re: [PATCH] loop file resizable


Andrew Morton:
> > --- linux-2.6.27/drivers/block/loop.c 7 Nov 2008 13:07:06 -0000 1.1
> > +++ linux-2.6.27/drivers/block/loop.c 8 Nov 2008 03:29:47 -0000
> > @@ -1142,6 +1142,8 @@ static int lo_ioctl(struct inode * inode
> > {
> > struct loop_device *lo = inode->i_bdev->bd_disk->private_data;
> > int err;
> > + sector_t sec;
> > + loff_t sz;
> >
> > mutex_lock(&lo->lo_ctl_mutex);
> > switch (cmd) {
> > @@ -1166,6 +1168,16 @@ static int lo_ioctl(struct inode * inode
> > case LOOP_GET_STATUS64:
> > err = loop_get_status64(lo, (struct loop_info64 __user *) arg);
> > break;
> > + case LOOP_SET_CAPACITY:
> > + err = figure_loop_size(lo);
> > + if (!err) {
>
> It would be neater to declare `sec' and `sz' here.

Here is the updated one.
- new function loop_set_capacity().
- return an error when the backend file is not set.


J. R. Okajima

----------------------------------------------------------------------

The following patch against loop.c adds ability to 'resize' the loop device on the fly.
This may be practically very useful.

One practical application is a loop file with XFS filesystem, already mounted:
You can easily enlarge the file (append some bytes) and then call
ioctl(fd, LOOP_SET_CAPACITY, new);
The loop driver will learn about the new size and you can use xfs_growfs
later on, which will allow you to use full capacity of the loop
file without the need to unmount.

Please include it in mainline kernel.

Hope you like it.

Signed-off-by: Junjiro Okajima <[email protected]>
Signed-off-by: Tomas Matejicek <[email protected]>

Index: linux-2.6.27/drivers/block/loop.c
===================================================================
retrieving revision 1.1
retrieving revision 1.3
diff -u -p -r1.1 -r1.3
--- linux-2.6.27/drivers/block/loop.c 7 Nov 2008 13:07:06 -0000 1.1
+++ linux-2.6.27/drivers/block/loop.c 19 Nov 2008 03:42:54 -0000 1.3
@@ -1137,6 +1137,28 @@ loop_get_status64(struct loop_device *lo
return err;
}

+static int loop_set_capacity(struct loop_device *lo, struct block_device *bdev)
+{
+ int err;
+ sector_t sec;
+ loff_t sz;
+
+ err = -ENXIO;
+ if (lo->lo_state != Lo_bound)
+ goto out;
+ err = figure_loop_size(lo);
+ if (err)
+ goto out;
+ sec = get_capacity(lo->lo_disk);
+ sz = sec << 9;
+ mutex_lock(&bdev->bd_mutex);
+ bd_set_size(bdev, sz);
+ mutex_unlock(&bdev->bd_mutex);
+
+ out:
+ return err;
+}
+
static int lo_ioctl(struct inode * inode, struct file * file,
unsigned int cmd, unsigned long arg)
{
@@ -1166,6 +1188,9 @@ static int lo_ioctl(struct inode * inode
case LOOP_GET_STATUS64:
err = loop_get_status64(lo, (struct loop_info64 __user *) arg);
break;
+ case LOOP_SET_CAPACITY:
+ err = loop_set_capacity(lo, inode->i_bdev);
+ break;
default:
err = lo->ioctl ? lo->ioctl(lo, cmd, arg) : -EINVAL;
}
@@ -1311,6 +1336,7 @@ static long lo_compat_ioctl(struct file
lo, (struct compat_loop_info __user *) arg);
mutex_unlock(&lo->lo_ctl_mutex);
break;
+ case LOOP_SET_CAPACITY:
case LOOP_CLR_FD:
case LOOP_GET_STATUS64:
case LOOP_SET_STATUS64:
Index: linux-2.6.27/include/linux/loop.h
===================================================================
retrieving revision 1.1
retrieving revision 1.2
diff -u -p -r1.1 -r1.2
--- linux-2.6.27/include/linux/loop.h 7 Nov 2008 13:05:58 -0000 1.1
+++ linux-2.6.27/include/linux/loop.h 8 Nov 2008 04:47:20 -0000 1.2
@@ -160,5 +160,6 @@ int loop_unregister_transfer(int number)
#define LOOP_SET_STATUS64 0x4C04
#define LOOP_GET_STATUS64 0x4C05
#define LOOP_CHANGE_FD 0x4C06
+#define LOOP_SET_CAPACITY 0x4C07

#endif

2008-11-19 18:33:10

by Tomas M

[permalink] [raw]
Subject: Re: [PATCH] loop file resizable

[email protected] wrote:
> Here is the updated one.
> - new function loop_set_capacity().
> - return an error when the backend file is not set.

Thank you very much.
Andrew, would you please consider merging this, it's short and working well.

Thank you.

Tomas M

2008-11-20 21:27:19

by Andrew Morton

[permalink] [raw]
Subject: Re: [PATCH] loop file resizable

On Wed, 19 Nov 2008 12:51:01 +0900
[email protected] wrote:

> The following patch against loop.c adds ability to 'resize' the loop device on the fly.
> This may be practically very useful.
>
> One practical application is a loop file with XFS filesystem, already mounted:
> You can easily enlarge the file (append some bytes) and then call
> ioctl(fd, LOOP_SET_CAPACITY, new);
> The loop driver will learn about the new size and you can use xfs_growfs
> later on, which will allow you to use full capacity of the loop
> file without the need to unmount.
>
> Please include it in mainline kernel.
>
> Hope you like it.
>
> Signed-off-by: Junjiro Okajima <[email protected]>
> Signed-off-by: Tomas Matejicek <[email protected]>
>
> Index: linux-2.6.27/drivers/block/loop.c
> ===================================================================
> retrieving revision 1.1
> retrieving revision 1.3
> diff -u -p -r1.1 -r1.3
> --- linux-2.6.27/drivers/block/loop.c 7 Nov 2008 13:07:06 -0000 1.1
> +++ linux-2.6.27/drivers/block/loop.c 19 Nov 2008 03:42:54 -0000 1.3
> @@ -1137,6 +1137,28 @@ loop_get_status64(struct loop_device *lo
> return err;
> }
>
> +static int loop_set_capacity(struct loop_device *lo, struct block_device *bdev)
> +{
> + int err;
> + sector_t sec;
> + loff_t sz;
> +
> + err = -ENXIO;
> + if (lo->lo_state != Lo_bound)
> + goto out;
> + err = figure_loop_size(lo);
> + if (err)
> + goto out;
> + sec = get_capacity(lo->lo_disk);
> + sz = sec << 9;
> + mutex_lock(&bdev->bd_mutex);
> + bd_set_size(bdev, sz);
> + mutex_unlock(&bdev->bd_mutex);
> +
> + out:
> + return err;
> +}
> +
> static int lo_ioctl(struct inode * inode, struct file * file,
> unsigned int cmd, unsigned long arg)
> {
> @@ -1166,6 +1188,9 @@ static int lo_ioctl(struct inode * inode
> case LOOP_GET_STATUS64:
> err = loop_get_status64(lo, (struct loop_info64 __user *) arg);
> break;
> + case LOOP_SET_CAPACITY:
> + err = loop_set_capacity(lo, inode->i_bdev);
> + break;
> default:
> err = lo->ioctl ? lo->ioctl(lo, cmd, arg) : -EINVAL;
> }
> @@ -1311,6 +1336,7 @@ static long lo_compat_ioctl(struct file
> lo, (struct compat_loop_info __user *) arg);
> mutex_unlock(&lo->lo_ctl_mutex);
> break;
> + case LOOP_SET_CAPACITY:
> case LOOP_CLR_FD:
> case LOOP_GET_STATUS64:
> case LOOP_SET_STATUS64:
> Index: linux-2.6.27/include/linux/loop.h
> ===================================================================
> retrieving revision 1.1
> retrieving revision 1.2
> diff -u -p -r1.1 -r1.2
> --- linux-2.6.27/include/linux/loop.h 7 Nov 2008 13:05:58 -0000 1.1
> +++ linux-2.6.27/include/linux/loop.h 8 Nov 2008 04:47:20 -0000 1.2
> @@ -160,5 +160,6 @@ int loop_unregister_transfer(int number)
> #define LOOP_SET_STATUS64 0x4C04
> #define LOOP_GET_STATUS64 0x4C05
> #define LOOP_CHANGE_FD 0x4C06
> +#define LOOP_SET_CAPACITY 0x4C07
>
> #endif

Well if we're going to do this then I guess we'll need to tell the
util-linux people about it, as users will be needing some tool with
which to access the new fucntionality.

Presumably that would be some enhancement to losetup, or perhaps a new
tool. Could the util-linux people please have a think about this and
comment?

Also, I'm not sure that the user interface is terribly good. The user
must extend the file size and then we tell the loop driver to use all
of the new file size. it would be better (more flexible) to allow
userspace to explicitly pass in the new size for the loop device.

But I'm not sure that this is worth bothering about.

2008-11-21 02:38:20

by J. R. Okajima

[permalink] [raw]
Subject: Re: [PATCH] loop file resizable


Andrew Morton:
> Well if we're going to do this then I guess we'll need to tell the
> util-linux people about it, as users will be needing some tool with
> which to access the new fucntionality.
>
> Presumably that would be some enhancement to losetup, or perhaps a new
> tool. Could the util-linux people please have a think about this and
> comment?
>
> Also, I'm not sure that the user interface is terribly good. The user
> must extend the file size and then we tell the loop driver to use all
> of the new file size. it would be better (more flexible) to allow
> userspace to explicitly pass in the new size for the loop device.
>
> But I'm not sure that this is worth bothering about.

Here is the utility named logrow.

J. R. Okajima

----------------------------------------------------------------------
/*
* Copyright (C) 2005-2008 Junjiro Okajima
*
* This program, aufs is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/

/*
* $Id: logrow.c,v 1.1 2008/11/17 01:59:40 sfjro Exp $
*/

#include <linux/fs.h>
#include <linux/loop.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define _GNU_SOURCE
#include <getopt.h>

char *me;

void usage(FILE *f)
{
fprintf(f, "%s [options] loop_dev [backend_file]\n"
"-s, --set new_size_in_bytes\n"
"\twhen backend_file is given, "
"it will be expanded too while keeping the original contents\n",
me);
}

struct option opts[] = {
{
.name = "set",
.has_arg = 1,
.flag = NULL,
.val = 's'
},
{
.name = "help",
.has_arg = 0,
.flag = NULL,
.val = 'h'
}
};

void err_size(char *name, __u64 old)
{
fprintf(stderr, "size must be larger than current %s (%llu)\n",
name, old);
}

int main(int argc, char *argv[])
{
int fd, err, c, i, bfd;
ssize_t ssz;
size_t sz;
__u64 old, new, append;
char a[BUFSIZ];
struct stat st;
FILE *out;
char *backend, *dev;

err = EINVAL;
out = stderr;
me = argv[0];
new = 0;
while ((c = getopt_long(argc, argv, "s:h", opts, &i)) != -1) {
switch (c) {
case 's':
errno = 0;
new = strtoull(optarg, NULL, 0);
if (errno) {
err = errno;
perror(argv[i]);
goto out;
}
break;

case 'h':
err = 0;
out = stdout;
goto err;

default:
perror(argv[i]);
goto err;
}
}

if (optind < argc)
dev = argv[optind++];
else
goto err;

fd = open(dev, O_RDONLY);
if (fd < 0) {
err = errno;
perror(dev);
goto out;
}

err = ioctl(fd, BLKGETSIZE64, &old);
if (err) {
err = errno;
perror("ioctl BLKGETSIZE64");
goto out;
}

if (!new) {
printf("%llu\n", old);
goto out;
}

if (new < old) {
err = EINVAL;
err_size(dev, old);
goto out;
}

if (optind < argc) {
backend = argv[optind++];
bfd = open(backend, O_WRONLY|O_APPEND);
if (bfd < 0) {
err = errno;
perror(backend);
goto out;
}
err = fstat(bfd, &st);
if (err) {
err = errno;
perror(backend);
goto out;
}
if (new < st.st_size) {
err = EINVAL;
err_size(backend, st.st_size);
goto out;
}
append = new - st.st_size;
sz = sizeof(a);
while (append > 0) {
if (append < sz)
sz = append;
ssz = write(bfd, a, sz);
if (ssz != sz) {
err = errno;
perror(backend);
goto out;
}
append -= sz;
}
err = fsync(bfd);
if (err) {
err = errno;
perror(backend);
goto out;
}
}

err = ioctl(fd, LOOP_SET_CAPACITY, new);
if (err) {
err = errno;
perror("ioctl LOOP_SET_CAPACITY");
}
goto out;

err:
usage(out);
out:
return err;
}

2008-11-21 03:19:24

by J. R. Okajima

[permalink] [raw]
Subject: Re: [PATCH] loop file resizable


[email protected]:
>
> Andrew Morton:
> > Well if we're going to do this then I guess we'll need to tell the
> > util-linux people about it, as users will be needing some tool with
> > which to access the new fucntionality.
:::

> Here is the utility named logrow.

I should left some notes here.
Several people uses truncate/ftruncate to expand the size of the backend
file. Actually I used to do it. In this version of logrow.c, I changed
it to write, in order to allocate the disk blocks.

If I issued truncate and expand the size of filesystem by xfs_growfs or
something, it would succeed. But when the filesystem which has the
backend file becomes full and the loopback mounted xfs wants more disk
blocks, it will return EIO or retry several times.
I believe it should return ENOSPC at once and changed to write garbages
and allocate the disk blocks.

Tomas, this is the point of the new version.


J. R. Okajima

2008-11-21 04:32:17

by J. R. Okajima

[permalink] [raw]
Subject: [PATCH v2] loop file resizable


Revise the patch to be available to the latest linux-2.6.git tree.

----------------------------------------------------------------------
Add the ability to 'resize' the loop device on the fly.
This may be practically very useful.

One practical application is a loop file with XFS filesystem, already mounted:
You can easily enlarge the file (append some bytes) and then call
ioctl(fd, LOOP_SET_CAPACITY, new);
The loop driver will learn about the new size and you can use xfs_growfs
later on, which will allow you to use full capacity of the loop
file without the need to unmount.

Signed-off-by: J. R. Okajima <[email protected]>
Signed-off-by: Tomas Matejicek <[email protected]>
---
drivers/block/loop.c | 26 ++++++++++++++++++++++++++
include/linux/loop.h | 1 +
2 files changed, 27 insertions(+), 0 deletions(-)

diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index 5c4ee70..a034bf9 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -1137,6 +1137,28 @@ loop_get_status64(struct loop_device *lo, struct loop_info64 __user *arg) {
return err;
}

+static int loop_set_capacity(struct loop_device *lo, struct block_device *bdev)
+{
+ int err;
+ sector_t sec;
+ loff_t sz;
+
+ err = -ENXIO;
+ if (unlikely(lo->lo_state != Lo_bound))
+ goto out;
+ err = figure_loop_size(lo);
+ if (unlikely(err))
+ goto out;
+ sec = get_capacity(lo->lo_disk);
+ sz = sec << 9;
+ mutex_lock(&bdev->bd_mutex);
+ bd_set_size(bdev, sz);
+ mutex_unlock(&bdev->bd_mutex);
+
+ out:
+ return err;
+}
+
static int lo_ioctl(struct block_device *bdev, fmode_t mode,
unsigned int cmd, unsigned long arg)
{
@@ -1166,6 +1188,9 @@ static int lo_ioctl(struct block_device *bdev, fmode_t mode,
case LOOP_GET_STATUS64:
err = loop_get_status64(lo, (struct loop_info64 __user *) arg);
break;
+ case LOOP_SET_CAPACITY:
+ err = loop_set_capacity(lo, bdev);
+ break;
default:
err = lo->ioctl ? lo->ioctl(lo, cmd, arg) : -EINVAL;
}
@@ -1311,6 +1336,7 @@ static int lo_compat_ioctl(struct block_device *bdev, fmode_t mode,
lo, (struct compat_loop_info __user *) arg);
mutex_unlock(&lo->lo_ctl_mutex);
break;
+ case LOOP_SET_CAPACITY:
case LOOP_CLR_FD:
case LOOP_GET_STATUS64:
case LOOP_SET_STATUS64:
diff --git a/include/linux/loop.h b/include/linux/loop.h
index 46169a7..e9e002a 100644
--- a/include/linux/loop.h
+++ b/include/linux/loop.h
@@ -160,5 +160,6 @@ int loop_unregister_transfer(int number);
#define LOOP_SET_STATUS64 0x4C04
#define LOOP_GET_STATUS64 0x4C05
#define LOOP_CHANGE_FD 0x4C06
+#define LOOP_SET_CAPACITY 0x4C07

#endif
--

2008-11-21 10:12:58

by Karel Zak

[permalink] [raw]
Subject: Re: [PATCH] loop file resizable

On Thu, Nov 20, 2008 at 01:26:54PM -0800, Andrew Morton wrote:
> Well if we're going to do this then I guess we'll need to tell the
> util-linux people about it, as users will be needing some tool with
> which to access the new fucntionality.
>
> Presumably that would be some enhancement to losetup, or perhaps a new
> tool. Could the util-linux people please have a think about this and
> comment?

It shouldn't be a problem to extend the current losetup(8). I have no
problem with this change.

> Also, I'm not sure that the user interface is terribly good. The user
> must extend the file size and then we tell the loop driver to use all
> of the new file size. it would be better (more flexible) to allow
> userspace to explicitly pass in the new size for the loop device.
>
> But I'm not sure that this is worth bothering about.

I think the interface is useful and kernel side is pretty simple.

Karel

--
Karel Zak <[email protected]>

2008-11-21 12:24:27

by J. R. Okajima

[permalink] [raw]
Subject: Re: [PATCH] loop file resizable


Karel Zak:
> > Presumably that would be some enhancement to losetup, or perhaps a new
> > tool. Could the util-linux people please have a think about this and
> > comment?
>
> It shouldn't be a problem to extend the current losetup(8). I have no
> problem with this change.

I have updated the userspace utility, logrow.c which I already posted
here.
Do you want me to post it here again?
If you tell me the URL of the base version of losetup, I will make a
patch.


J. R. Okajima

2008-11-21 12:44:51

by Karel Zak

[permalink] [raw]
Subject: Re: [PATCH] loop file resizable

On Fri, Nov 21, 2008 at 09:23:48PM +0900, [email protected] wrote:
>
> Karel Zak:
> > It shouldn't be a problem to extend the current losetup(8). I have no
> > problem with this change.
>
> If you tell me the URL of the base version of losetup, I will make a
> patch.

git://git.kernel.org/pub/scm/utils/util-linux-ng/util-linux-ng.git

Note, that I'll commit the patch when the kernel side will be in
Linus's tree.

Karel

--
Karel Zak <[email protected]>

2008-11-24 02:08:40

by J. R. Okajima

[permalink] [raw]
Subject: Re: [PATCH] loop file resizable


Karel Zak:
> On Fri, Nov 21, 2008 at 09:23:48PM +0900, [email protected] wrote:
:::
> > If you tell me the URL of the base version of losetup, I will make a
> > patch.
>
> git://git.kernel.org/pub/scm/utils/util-linux-ng/util-linux-ng.git

Thanx.


> Note, that I'll commit the patch when the kernel side will be in
> Linus's tree.

Sure.
Here is the patch.

J. R. Okajima

----------------------------------------------------------------------

Subject: [PATCH 1/1] grow the size of loopback device and its backend file

introduce a new option, "-g | --grow <new_size> <loopdev> [file]" which
requres the kernel patch titled
"Subject: + loop-add-ioctl-to-resize-a-loop-device.patch added to -mm tree"
on 21 Nov 2008.

Signed-off-by: J. R. Okajima <[email protected]>
---
mount/lomount.c | 154 +++++++++++++++++++++++++++++++++++++++++++++++++++++--
mount/loop.h | 2 +
2 files changed, 152 insertions(+), 4 deletions(-)

diff --git a/mount/lomount.c b/mount/lomount.c
index 5675eac..c705b43 100644
--- a/mount/lomount.c
+++ b/mount/lomount.c
@@ -24,6 +24,7 @@
#include "xmalloc.h"
#include "realpath.h"
#include "pathnames.h"
+#include "blkdev.h"

#define SIZE(a) (sizeof(a)/sizeof(a[0]))

@@ -827,6 +828,136 @@ del_loop (const char *device) {
return 0;
}

+void grow_err(const char *name, unsigned long long old, unsigned long long new)
+{
+ fprintf(stderr, "size (%llu) must be larger than current %s (%llu)\n",
+ new, name, old);
+}
+
+int grow_file(const char *file, unsigned long long new)
+{
+ int err, fd;
+ unsigned long long append;
+ size_t sz;
+ ssize_t ssz;
+ const size_t one_g = 1 << 30;
+ struct stat st;
+ char *p;
+
+ err = -1;
+ fd = open(file, O_WRONLY | O_APPEND);
+ if (fd < 0)
+ goto out_p;
+
+ err = fstat(fd, &st);
+ if (err)
+ goto out_p;
+
+ err = -1;
+ if (new < st.st_size) {
+ grow_err(file, st.st_size, new);
+ goto out;
+ }
+
+ append = new - st.st_size;
+ sz = append;
+ if (sz > one_g)
+ sz = one_g;
+ while (1) {
+ p = calloc(sz, 1);
+ if (p)
+ break;
+ sz >>= 1;
+ if (!sz) {
+ errno = ENOMEM;
+ goto out_p;
+ }
+ }
+
+ /*
+ * instread of ftrundate(2),
+ * allocate disk blocks to support ENOSPC
+ * on the loopback mounted filesystem
+ */
+ err = 0;
+ while (append > 0) {
+ if (append < sz)
+ sz = append;
+ ssz = write(fd, p, sz);
+ if (ssz == -1) {
+ if (errno == EAGAIN || errno == EINTR)
+ continue;
+ err = errno;
+ break;
+ }
+ append -= ssz;
+ }
+ free(p);
+ if (err) {
+ errno = err;
+ goto out_p;
+ }
+
+ err = fsync(fd);
+ if (err)
+ goto out_p;
+ err = close(fd);
+ if (!err)
+ goto out; /* success */
+
+ out_p:
+ perror(file);
+ out:
+ return err;
+}
+
+int grow_capacity(unsigned long long new, const char *device,
+ const char *file)
+{
+ int err, fd;
+ unsigned long long old;
+
+ fd = open(device, O_RDONLY);
+ if (fd < 0) {
+ err = errno;
+ perror(device);
+ goto out;
+ }
+
+ err = ioctl(fd, BLKGETSIZE64, &old);
+ if (err) {
+ err = errno;
+ perror("ioctl BLKGETSIZE64");
+ goto out;
+ }
+
+ if (!new) {
+ printf("%llu\n", old);
+ goto out;
+ }
+
+ if (new < old) {
+ err = EINVAL;
+ grow_err(device, old, new);
+ goto out;
+ }
+
+ if (file) {
+ err = grow_file(file, new);
+ if (err)
+ goto out;
+ }
+
+ err = ioctl(fd, LOOP_SET_CAPACITY, new);
+ if (err) {
+ err = errno;
+ perror("ioctl LOOP_SET_CAPACITY");
+ }
+
+ out:
+ return err;
+}
+
#else /* no LOOP_SET_FD defined */
static void
mutter(void) {
@@ -872,6 +1003,7 @@ usage(void) {
" %1$s -d | --detach <loopdev> delete\n"
" %1$s -f | --find find unused\n"
" %1$s -j | --associated <file> [-o <num>] list all associated with <file>\n"
+ " %1$s -g | --grow <new_size> <loopdev> [file] grow capacity\n"
" %1$s [ options ] {-f|--find|loopdev} <file> setup\n"),
progname);

@@ -889,13 +1021,14 @@ usage(void) {

int
main(int argc, char **argv) {
- char *p, *offset, *sizelimit, *encryption, *passfd, *device, *file, *assoc;
+ char *p, *offset, *sizelimit, *encryption, *passfd, *device, *file,
+ *assoc, *grow;
int delete, find, c, all;
int res = 0;
int showdev = 0;
int ro = 0;
int pfd = -1;
- unsigned long long off, slimit;
+ unsigned long long off, slimit, new_size;
struct option longopts[] = {
{ "all", 0, 0, 'a' },
{ "detach", 0, 0, 'd' },
@@ -903,6 +1036,7 @@ main(int argc, char **argv) {
{ "find", 0, 0, 'f' },
{ "help", 0, 0, 'h' },
{ "associated", 1, 0, 'j' },
+ { "grow", 1, 0, 'g' },
{ "offset", 1, 0, 'o' },
{ "sizelimit", 1, 0, 128 },
{ "pass-fd", 1, 0, 'p' },
@@ -919,13 +1053,13 @@ main(int argc, char **argv) {
delete = find = all = 0;
off = 0;
slimit = 0;
- assoc = offset = sizelimit = encryption = passfd = NULL;
+ assoc = offset = sizelimit = encryption = passfd = grow = NULL;

progname = argv[0];
if ((p = strrchr(progname, '/')) != NULL)
progname = p+1;

- while ((c = getopt_long(argc, argv, "ade:E:fhj:o:p:rsv",
+ while ((c = getopt_long(argc, argv, "ade:E:fg:hj:o:p:rsv",
longopts, NULL)) != -1) {
switch (c) {
case 'a':
@@ -944,6 +1078,9 @@ main(int argc, char **argv) {
case 'f':
find = 1;
break;
+ case 'g':
+ grow = optarg;
+ break;
case 'j':
assoc = optarg;
break;
@@ -984,6 +1121,10 @@ main(int argc, char **argv) {
} else if (assoc) {
if (encryption || showdev || passfd || ro)
usage();
+ } else if (grow) {
+ if (all || assoc || find
+ || (argc != 4 && argc != 5))
+ usage();
} else {
if (argc < optind+1 || argc > optind+2)
usage();
@@ -995,6 +1136,9 @@ main(int argc, char **argv) {
if (sizelimit && sscanf(sizelimit, "%llu", &slimit) != 1)
usage();

+ if (grow && sscanf(grow, "%llu", &new_size) != 1)
+ usage();
+
if (all)
return show_used_loop_devices();
else if (assoc)
@@ -1020,6 +1164,8 @@ main(int argc, char **argv) {

if (delete)
res = del_loop(device);
+ else if (grow)
+ res = grow_capacity(new_size, device, file);
else if (file == NULL)
res = show_loop(device);
else {
diff --git a/mount/loop.h b/mount/loop.h
index 6068852..c6d244e 100644
--- a/mount/loop.h
+++ b/mount/loop.h
@@ -22,6 +22,8 @@
#define LOOP_GET_STATUS 0x4C03
#define LOOP_SET_STATUS64 0x4C04
#define LOOP_GET_STATUS64 0x4C05
+/* #define LOOP_CHANGE_FD 0x4C06 */
+#define LOOP_SET_CAPACITY 0x4C07

/* Flags for loop_into{64,}->lo_flags */
enum {
--
1.5.5.4.dirty