Loop device over NFS (not so difficult ?)

rohloff@informatik.tu-muenchen.de
Thu, 22 Jul 1999 23:21:22 +0000


Hi,

I just read at the Kernel Traffic site, that some discussion
was going on, concerning loop devices over NFS...

I THINK that I have the patch for this for quite some time
now... I also read that the solution should be quite difficult,
which in my case it isn't so it might be wrong...

Please have a look and judge for yourself
(This patch also affects loop.h, because it includes support
for automatically loading crypting modules... As I am posting
from Germany I think this shouldn't be illegal ?):

----------------------------SNIP--------------------------------
diff -u --recursive --new-file linux-2.2.10/drivers/block/loop.c linux-2.2.10-crypt/drivers/block/loop.c
--- linux-2.2.10/drivers/block/loop.c Sun May 30 17:17:03 1999
+++ linux-2.2.10-crypt/drivers/block/loop.c Thu Jul 15 22:14:27 1999
@@ -42,7 +42,11 @@

#include <asm/uaccess.h>

-#include <linux/loop.h>
+#include <linux/loop.h>
+
+#if defined(CONFIG_KMOD)
+#include <linux/kmod.h>
+#endif

#define MAJOR_NR LOOP_MAJOR

@@ -63,10 +67,6 @@
#define FALSE 0
#define TRUE (!FALSE)

-/* Forward declaration of function to create missing blocks in the
- backing file (can happen if the backing file is sparse) */
-static int create_missing_block(struct loop_device *lo, int block, int blksize);
-
/*
* Transfer functions
*/
@@ -152,12 +152,16 @@

static void do_lo_request(void)
{
- int real_block, block, offset, len, blksize, size;
- char *dest_addr;
+ int block, offset, len, blksize, size, sector;
+ char *dest_addr,block_buf[4096];
struct loop_device *lo;
struct buffer_head *bh;
struct request *current_request;
- int block_present;
+ struct file* file;
+ int retval;
+ loff_t f_offset;
+ mm_segment_t old_fs;
+ struct inode *inode;

repeat:
INIT_REQUEST;
@@ -178,6 +182,7 @@

dest_addr = current_request->buffer;

+ sector=current_request->sector;
if (blksize < 512) {
block = current_request->sector * (512/blksize);
offset = 0;
@@ -207,32 +212,65 @@
if (size > len)
size = len;

- real_block = block;
- block_present = TRUE;
-
- if (lo->lo_flags & LO_FLAGS_DO_BMAP) {
- real_block = bmap(lo->lo_dentry->d_inode, block);
- if (!real_block) {
-
- /* The backing file is a sparse file and this block
- doesn't exist. If reading, return zeros. If
- writing, force the underlying FS to create
- the block */
- if (current_request->cmd == READ) {
- memset(dest_addr, 0, size);
- block_present = FALSE;
- } else {
- if (!create_missing_block(lo, block, blksize)) {
- goto error_out_lock;
- }
- real_block = bmap(lo->lo_dentry->d_inode, block);
+ if (lo->lo_backing_file) { /* If we access a file use file ops to */
+ /* access the data */
+ f_offset = block * blksize + offset;
+ file = lo->lo_backing_file;
+
+ if (file->f_op == NULL) {
+ printk(KERN_WARNING "loop: no file ops\n");
+ goto error_out_lock;
+ }
+
+ if (file->f_op->llseek != NULL) {
+ file->f_op->llseek(file, f_offset, 0);
+ }
+ else {
+ /* Do what the default llseek() code would have done */
+ file->f_pos = f_offset;
+ file->f_reada = 0;
+ file->f_version = ++event; /* What's this ??? */
+ }
+
+ if (current_request->cmd==READ) {
+ old_fs = get_fs();
+ set_fs(get_ds());
+
+ retval=file->f_op->read(file,block_buf,size,&file->f_pos);
+
+ set_fs(old_fs);
+
+ if (retval < 0) {
+ printk(KERN_ERR "loop: cannot read block!");
+ goto error_out_lock;
}
-
}
- }
+
+ if ((lo->transfer)(lo, current_request->cmd, block_buf,
+ dest_addr, size, sector)) {
+ printk(KERN_ERR "loop: transfer error block %d\n", block);
+ goto error_out_lock;
+ }
+
+ if (current_request->cmd==WRITE) {
+ old_fs = get_fs();
+ set_fs(get_ds());
+ inode = file->f_dentry->d_inode;
+ down(&inode->i_sem);
+
+ retval = file->f_op->write(file, block_buf, size, &file->f_pos);
+
+ up(&inode->i_sem);
+ set_fs(old_fs);
+
+ if (retval < 0) {
+ printk(KERN_ERR "loop: cannot write block!");
+ goto error_out_lock;
+ }
+ }
+ } else { /* It is a block device, so use block driver routines */
+ bh = getblk(lo->lo_device, block, blksize);

- if (block_present) {
- bh = getblk(lo->lo_device, real_block, blksize);
if (!bh) {
printk(KERN_ERR "loop: device %s: getblk(-, %d, %d) returned NULL",
kdevname(lo->lo_device),
@@ -250,7 +288,7 @@
}

if ((lo->transfer)(lo, current_request->cmd, bh->b_data + offset,
- dest_addr, size, real_block)) {
+ dest_addr, size, sector)) {
printk(KERN_ERR "loop: transfer error block %d\n", block);
brelse(bh);
goto error_out_lock;
@@ -281,61 +319,6 @@
goto repeat;
}

-static int create_missing_block(struct loop_device *lo, int block, int blksize)
-{
- struct file *file;
- loff_t new_offset;
- char zero_buf[1] = { 0 };
- ssize_t retval;
- mm_segment_t old_fs;
- struct inode *inode;
-
- file = lo->lo_backing_file;
- if (file == NULL) {
- printk(KERN_WARNING "loop: cannot create block - no backing file\n");
- return FALSE;
- }
-
- if (file->f_op == NULL) {
- printk(KERN_WARNING "loop: cannot create block - no file ops\n");
- return FALSE;
- }
-
- new_offset = block * blksize;
-
- if (file->f_op->llseek != NULL) {
- file->f_op->llseek(file, new_offset, 0);
- } else {
- /* Do what the default llseek() code would have done */
- file->f_pos = new_offset;
- file->f_reada = 0;
- file->f_version = ++event;
- }
-
- if (file->f_op->write == NULL) {
- printk(KERN_WARNING "loop: cannot create block - file not writeable\n");
- return FALSE;
- }
-
- old_fs = get_fs();
- set_fs(get_ds());
-
- inode = file->f_dentry->d_inode;
- down(&inode->i_sem);
- retval = file->f_op->write(file, zero_buf, 1, &file->f_pos);
- up(&inode->i_sem);
-
- set_fs(old_fs);
-
- if (retval < 0) {
- printk(KERN_WARNING "loop: cannot create block - FS write failed: code %d\n",
- retval);
- return FALSE;
- } else {
- return TRUE;
- }
-}
-
static int loop_set_fd(struct loop_device *lo, kdev_t dev, unsigned int arg)
{
struct file *file;
@@ -503,9 +486,15 @@
return -EFAULT;
if ((unsigned int) info.lo_encrypt_key_size > LO_KEY_SIZE)
return -EINVAL;
- type = info.lo_encrypt_type;
- if (info.lo_encrypt_key_size == 0 && type == LO_CRYPT_XOR)
- return -EINVAL;
+ type = info.lo_encrypt_type;
+
+#if defined(CONFIG_KMOD)
+ if (type < MAX_LO_CRYPT && xfer_funcs[type] == NULL)
+ { if (crypt_module_names[type] != NULL)
+ request_module(crypt_module_names[type]);
+ }
+#endif
+
if (type >= MAX_LO_CRYPT || xfer_funcs[type] == NULL)
return -EINVAL;
err = loop_release_xfer(lo);
diff -u --recursive --new-file linux-2.2.10/include/linux/loop.h linux-2.2.10-crypt/include/linux/loop.h
--- linux-2.2.10/include/linux/loop.h Mon Nov 23 05:29:54 1998
+++ linux-2.2.10-crypt/include/linux/loop.h Sat May 15 19:42:50 1999
@@ -98,6 +98,19 @@
#define LO_CRYPT_SKIPJACK 10
#define MAX_LO_CRYPT 20

+static char *crypt_module_names[20]= { NULL, /* NONE */
+ NULL, /* XOR */
+ "des",
+ "fish2",
+ "blow",
+ "cast",
+ "idea",
+ NULL,NULL,NULL,
+ "skipjack",
+ NULL,NULL,NULL,NULL,NULL,NULL,
+ NULL,NULL,NULL
+ };
+
#ifdef __KERNEL__
/* Support for loadable transfer modules */
struct loop_func_table {

------------------------------------SNIP-----------------------------

By the way, the ac-10 patch contains the following warning:

* WARNING/FIXME:
* - The block number as IV passing to low level transfer functions is broken:
* it passes the underlying device's block number instead of the
* offset. This makes it change for a given block when the file is
* moved/restored/copied and also doesn't work over NFS.
*/

This isn't true (as far as I can figure).
It is true that the block changes as the file moves around, but this
doesn't matter...

1st case: you access a block device:

That means loop sector == block sector

so there is no problem. If you read a sector from the block device
it will always be decoded with the same IV value... If some CONTENT
of the block is moved, it doesn't matter, as long as the decoding
process for a given sectos isn't changed.

For a file the warning is also NOT true. Basically for the same reasons:
What is used for the IV value is the sector given to the loop device,
and so it doesn't matter if you move content. This only means that
the encoding scheme changes for the content, but the encoding scheme
for the sectors always stays the same, and that's what is important.

(Well... I admit what I say isn't really clear... If there is still
doubt I will try again... but first think about it a little harder)

Another thing: Is there a way to turn off buffer caching for a block
device ? I believe at the moment some double caching takes place:
Since the loop device is a block device itself, I guess that the
blocks of the loop device are cached AND the blocks of the underlying
raw device are cached also. This shouldn't be necessary...

so long
Ingo

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/