aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/block
diff options
context:
space:
mode:
authorJ. R. Okajima <hooanon05@yahoo.co.jp>2009-03-31 18:23:43 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2009-04-01 11:59:17 -0400
commit53d6660836f233df66490707365ab177e5fb2bb4 (patch)
treedad7eae486c0928af039f4bb47ee3dd7bb0ff87d /drivers/block
parent65bd6a9bc7be3f5841dad12f77ce4b3210bd82c5 (diff)
loop: add ioctl to resize a loop device
Add the ability to 'resize' the loop device on the fly. 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. Test app: #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; } Signed-off-by: J. R. Okajima <hooanon05@yahoo.co.jp> Signed-off-by: Tomas Matejicek <tomas@slax.org> Cc: <util-linux-ng@vger.kernel.org> Cc: Karel Zak <kzak@redhat.com> Cc: Jens Axboe <jens.axboe@oracle.com> Cc: Al Viro <viro@zeniv.linux.org.uk> Cc: Christoph Hellwig <hch@lst.de> Cc: Akinobu Mita <akinobu.mita@gmail.com> Cc: <linux-api@vger.kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/block')
-rw-r--r--drivers/block/loop.c30
1 files changed, 30 insertions, 0 deletions
diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index 2621ed2ce6d2..40b17d3b55a1 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -1192,6 +1192,30 @@ loop_get_status64(struct loop_device *lo, struct loop_info64 __user *arg) {
1192 return err; 1192 return err;
1193} 1193}
1194 1194
1195static int loop_set_capacity(struct loop_device *lo, struct block_device *bdev)
1196{
1197 int err;
1198 sector_t sec;
1199 loff_t sz;
1200
1201 err = -ENXIO;
1202 if (unlikely(lo->lo_state != Lo_bound))
1203 goto out;
1204 err = figure_loop_size(lo);
1205 if (unlikely(err))
1206 goto out;
1207 sec = get_capacity(lo->lo_disk);
1208 /* the width of sector_t may be narrow for bit-shift */
1209 sz = sec;
1210 sz <<= 9;
1211 mutex_lock(&bdev->bd_mutex);
1212 bd_set_size(bdev, sz);
1213 mutex_unlock(&bdev->bd_mutex);
1214
1215 out:
1216 return err;
1217}
1218
1195static int lo_ioctl(struct block_device *bdev, fmode_t mode, 1219static int lo_ioctl(struct block_device *bdev, fmode_t mode,
1196 unsigned int cmd, unsigned long arg) 1220 unsigned int cmd, unsigned long arg)
1197{ 1221{
@@ -1224,6 +1248,11 @@ static int lo_ioctl(struct block_device *bdev, fmode_t mode,
1224 case LOOP_GET_STATUS64: 1248 case LOOP_GET_STATUS64:
1225 err = loop_get_status64(lo, (struct loop_info64 __user *) arg); 1249 err = loop_get_status64(lo, (struct loop_info64 __user *) arg);
1226 break; 1250 break;
1251 case LOOP_SET_CAPACITY:
1252 err = -EPERM;
1253 if ((mode & FMODE_WRITE) || capable(CAP_SYS_ADMIN))
1254 err = loop_set_capacity(lo, bdev);
1255 break;
1227 default: 1256 default:
1228 err = lo->ioctl ? lo->ioctl(lo, cmd, arg) : -EINVAL; 1257 err = lo->ioctl ? lo->ioctl(lo, cmd, arg) : -EINVAL;
1229 } 1258 }
@@ -1371,6 +1400,7 @@ static int lo_compat_ioctl(struct block_device *bdev, fmode_t mode,
1371 lo, (struct compat_loop_info __user *) arg); 1400 lo, (struct compat_loop_info __user *) arg);
1372 mutex_unlock(&lo->lo_ctl_mutex); 1401 mutex_unlock(&lo->lo_ctl_mutex);
1373 break; 1402 break;
1403 case LOOP_SET_CAPACITY:
1374 case LOOP_CLR_FD: 1404 case LOOP_CLR_FD:
1375 case LOOP_GET_STATUS64: 1405 case LOOP_GET_STATUS64:
1376 case LOOP_SET_STATUS64: 1406 case LOOP_SET_STATUS64: