lkml.org 
[lkml]   [2001]   [Feb]   [13]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
Date
From
Subject[RFC][PATCH] block ioctl to read/write last sector (New! Improved!)
To address the concerns of Andi Kleen, with a good suggestion from Richard
Johnson, I have revised my previous patch attempt. Please check this out
and comment.

Changelog:
1) use get_gendisk() instead of walking array manually
2) pass in struct instead of guessing..
+ struct {
+ unsigned int block;
+ size_t content_length;
+ char *block_contents;
+ } blk_ioctl_parameter;

3) On write, read in previous contents in case userspace doesn't modify
the whole block. (prevents garbage from being put in the remainder of the
sector)


On 7 Feb 2001, Andi Kleen wrote:
> But what happens when you e.g. run a software blocksize of 4096 and the device
> has >1 inaccessible 512 byte sector at the end?
> I think it would be better to pass in a offset in 512 byte units to a special
> ioctl (and do error checking in the driver for impossible requests)

This has been addressed in the new patch. The new patch takes in an LBA
offset and reads "LastLBA - offset" and returns the result to userspace.
Checks are done so that only normally inaccessable blocks are accessible
through this method. This is done to prevent any possible security
consequences from arising because of this patch.

Test code showing how to use this IOCTL have also been attached.
Regards,
Michael Brown


------Original Message------
Problem Summary:
There is no function exported to userspace to read or write the last
512-byte sector of an odd-size disk.

The block device uses 1K blocksize, and will prevent userspace from
seeing the odd-block at the end of the disk, if the disk is odd-size.

IA-64 architecture defines a new partitioning scheme where there is a
backup of the partition table header in the last sector of the disk. While
we can read and write to this sector in the kernel partition code, we have
no way for userspace to update this partition block.

Solution:
As an interim solution, I propose the following IOCTLs for the block
device layer: BLKGETLASTSECT and BLKSETLASTSECT. These ioctls will take a
userspace pointer to a char[512] and read/write the last sector. Below is
a patch to do this.

I have attached the patch as well, because I've heard that Pine will eat
patches. :-(





#include <sys/types.h>
#include <sys/stat.h>
#include <sys/fcntl.h>
#include <stdio.h>

#include <sys/ioctl.h>

#define BLKGETLASTSECT _IO(0x12,108) /* get last sector of block device */
#define BLKSETLASTSECT _IO(0x12,109) /* get last sector of block device */
#define BLKSSZGET _IO(0x12,104)/* get block device sector size */

struct blkdev_ioctl_param {
unsigned int block;
size_t content_length;
char * block_contents;
};

main(int argc, char *argv[])
{
int filedes, rc=0, blksz=0;
char *lastblock;
struct blkdev_ioctl_param ioctl_param;

ioctl_param.block = 0;

filedes = open("/dev/sdb", O_RDONLY );
printf(" open filedes: %d\n", filedes);

rc = ioctl(filedes, BLKSSZGET, &blksz) ;
lastblock = (char *) malloc( blksz + 8 );
ioctl_param.content_length = blksz;
printf(" block size appears to be %d\n", blksz);

memset( lastblock, 'z', blksz + 8 );
ioctl_param.block_contents = lastblock;

rc = ioctl(filedes, BLKGETLASTSECT, &ioctl_param) ;
printf(" last sect ioctl rc: %d\n", rc);

lastblock[blksz + 1] = '\0';
printf(" Last Block: \n%s<--\n", lastblock);

return 0;
}


#include <sys/types.h>
#include <sys/stat.h>
#include <sys/fcntl.h>
#include <stdio.h>

#include <sys/ioctl.h>

#define BLKGETLASTSECT _IO(0x12,108) /* get last sector of block device */
#define BLKSETLASTSECT _IO(0x12,109) /* get last sector of block device */
#define BLKSSZGET _IO(0x12,104)/* get block device sector size */

struct blkdev_ioctl_param {
unsigned int block;
size_t content_length;
char * block_contents;
};

main(int argc, char *argv[])
{
int filedes, rc=0, blksz=0;
char *lastblock, newchar = 'a';
struct blkdev_ioctl_param ioctl_param;

if( argc > 1 ) newchar = argv[1][0];

ioctl_param.block = 0;

filedes = open("/dev/sdb", O_RDONLY );
printf(" open filedes: %d\n", filedes);

rc = ioctl(filedes, BLKSSZGET, &blksz) ;
lastblock = (char *) malloc( blksz + 2 );
ioctl_param.content_length = blksz ;
printf(" block size appears to be %d\n", blksz );

memset( lastblock, newchar, blksz + 2 );
ioctl_param.block_contents = lastblock;

rc = ioctl(filedes, BLKSETLASTSECT, &ioctl_param) ;
printf(" last sect ioctl rc: %d\n", rc);

return 0;
}

diff -ruP linux/drivers/block/blkpg.c linux-ioctl/drivers/block/blkpg.c
--- linux/drivers/block/blkpg.c Fri Oct 27 01:35:47 2000
+++ linux-ioctl/drivers/block/blkpg.c Tue Feb 13 11:39:37 2001
@@ -39,6 +39,9 @@

#include <asm/uaccess.h>

+static int set_last_sector( kdev_t dev, const void *param );
+static int get_last_sector( kdev_t dev, const void *param );
+
/*
* What is the data describing a partition?
*
@@ -210,6 +213,16 @@
int intval;

switch (cmd) {
+ case BLKGETLASTSECT:
+ return get_last_sector(dev, (char *)(arg));
+
+ case BLKSETLASTSECT:
+ if( is_read_only(dev) )
+ return -EACCES;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EACCES;
+ return set_last_sector(dev, (char *)(arg));
+
case BLKROSET:
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
@@ -281,3 +294,209 @@
}

EXPORT_SYMBOL(blk_ioctl);
+
+ /*********************
+ * get_last_sector()
+ *
+ * Description: This function will read any inaccessible blocks at the end
+ * of a device
+ * Why: Normal read/write calls through the block layer will not read the
+ * last sector of an odd-size disk.
+ * parameters:
+ * dev: kdev_t -- which device to read
+ * param: a pointer to a userspace struct. The struct has these members:
+ * block: an int which denotes which block to return:
+ * 0 == Last block
+ * 1 == Last block - 1
+ * n == Last block - n
+ * This is validated so that only values of
+ * <= ((total_sects + 1) % logical_block_size) || 0
+ * are allowed.
+ * block_contents: a pointer to userspace char*, this is where we write
+ * returned blocks to.
+ * content_length: How big the userspace buffer is.
+ * return:
+ * 0 on success
+ * -ERRVAL on error.
+ *********************/
+int get_last_sector( kdev_t dev, const void *param )
+{
+ struct buffer_head *bh;
+ struct gendisk *g;
+ int rc = 0;
+ unsigned int lastlba, readlba;
+ int orig_blksize = BLOCK_SIZE;
+ int hardblocksize;
+
+ struct {
+ unsigned int block;
+ size_t content_length;
+ char *block_contents;
+ } blk_ioctl_parameter;
+
+ if( !dev ) return -EINVAL;
+
+ if(copy_from_user(&blk_ioctl_parameter, param, sizeof(blk_ioctl_parameter)))
+ return -EFAULT;
+
+ g = get_gendisk( dev );
+
+ if( !g ) return -EINVAL;
+
+ lastlba = g->part[MINOR(dev)].nr_sects;
+
+ if( !lastlba ) return -EINVAL;
+
+ hardblocksize = get_hardblocksize(dev);
+ if( ! hardblocksize ) hardblocksize = 512;
+
+ /* Need to change the block size that the block layer uses */
+ if (blksize_size[MAJOR(dev)]){
+ orig_blksize = blksize_size[MAJOR(dev)][MINOR(dev)];
+ }
+
+ /* validate userspace input */
+ if( blk_ioctl_parameter.block == 0 )
+ goto good_params;
+
+ /* so we don't divide by zero below */
+ if(orig_blksize == 0)
+ return -EINVAL;
+
+ if( blk_ioctl_parameter.block <= (lastlba % (orig_blksize / hardblocksize)))
+ goto good_params;
+
+ return -EINVAL;
+
+good_params:
+ readlba = lastlba - blk_ioctl_parameter.block - 1;
+
+ if (orig_blksize != hardblocksize)
+ set_blocksize(dev, hardblocksize);
+
+ bh = bread(dev, readlba, hardblocksize);
+ if (!bh) {
+ /* We hit the end of the disk */
+ printk(KERN_WARNING
+ "get_last_sector ioctl: bread returned NULL.\n");
+ return -1;
+ }
+
+ rc = copy_to_user(blk_ioctl_parameter.block_contents, bh->b_data,
+ (bh->b_size > blk_ioctl_parameter.content_length) ?
+ blk_ioctl_parameter.content_length : bh->b_size);
+
+ brelse(bh);
+
+ /* change block size back */
+ if (orig_blksize != hardblocksize)
+ set_blocksize(dev, orig_blksize);
+
+ return rc;
+}
+
+ /*********************
+ * set_last_sector()
+ *
+ * Description: This function will write to any inaccessible blocks at the end
+ * of a device
+ * Why: Normal read/write calls through the block layer will not read the
+ * last sector of an odd-size disk.
+ * parameters:
+ * dev: kdev_t -- which device to read
+ * sect: a pointer to a userspace struct. The struct has these members:
+ * block: an int which denotes which block to return:
+ * 0 == Last block
+ * 1 == Last block - 1
+ * n == Last block - n
+ * This is validated so that only values of
+ * <= ((total_sects + 1) % logical_block_size) || 0
+ * are allowed.
+ * block_contents: a pointer to userspace char*, this is where we write
+ * returned blocks to.
+ * content_length: How big the userspace buffer is.
+ * return:
+ * 0 on success
+ * -ERRVAL on error.
+ *********************/
+int set_last_sector( kdev_t dev, const void *param )
+{
+ struct buffer_head *bh;
+ struct gendisk *g;
+ int rc = 0;
+ unsigned int lastlba, writelba;
+ int orig_blksize = BLOCK_SIZE;
+ int hardblocksize;
+
+ struct {
+ unsigned int block;
+ size_t content_length;
+ char *block_contents;
+ } blk_ioctl_parameter;
+
+ if( !dev ) return -EINVAL;
+
+ if(copy_from_user(&blk_ioctl_parameter, param, sizeof(blk_ioctl_parameter)))
+ return -EFAULT;
+
+ g = get_gendisk( dev );
+
+ if( !g ) return -EINVAL;
+
+ lastlba = g->part[MINOR(dev)].nr_sects ;
+
+ if( !lastlba ) return -EINVAL;
+
+ hardblocksize = get_hardblocksize(dev);
+ if( ! hardblocksize ) hardblocksize = 512;
+
+ /* Need to change the block size that the block layer uses */
+ if (blksize_size[MAJOR(dev)]){
+ orig_blksize = blksize_size[MAJOR(dev)][MINOR(dev)];
+ }
+
+ /* validate userspace input */
+ if( blk_ioctl_parameter.block == 0 )
+ goto good_params;
+
+ /* so we don't divide by zero below */
+ if(orig_blksize == 0)
+ return -EINVAL;
+
+ if( blk_ioctl_parameter.block <= (lastlba % (orig_blksize / hardblocksize)))
+ goto good_params;
+
+ return -EINVAL;
+
+good_params:
+ writelba = lastlba - blk_ioctl_parameter.block - 1;
+
+ if (orig_blksize != hardblocksize)
+ set_blocksize(dev, hardblocksize);
+
+ bh = bread(dev, writelba, hardblocksize);
+ if (!bh) {
+ /* We hit the end of the disk */
+ printk(KERN_WARNING
+ "get_last_sector ioctl: getblk returned NULL.\n");
+ return -1;
+ }
+
+ copy_from_user(bh->b_data, blk_ioctl_parameter.block_contents,
+ (bh->b_size > blk_ioctl_parameter.content_length) ?
+ blk_ioctl_parameter.content_length : bh->b_size);
+
+ mark_buffer_dirty(bh);
+ ll_rw_block (WRITE, 1, &bh);
+ wait_on_buffer (bh);
+ if (!buffer_uptodate(bh))
+ rc=-1;
+
+ brelse(bh);
+
+ /* change block size back */
+ if (orig_blksize != hardblocksize)
+ set_blocksize(dev, orig_blksize);
+
+ return rc;
+}
diff -ruP linux/drivers/ide/ide.c linux-ioctl/drivers/ide/ide.c
--- linux/drivers/ide/ide.c Wed Dec 6 14:06:19 2000
+++ linux-ioctl/drivers/ide/ide.c Tue Feb 13 11:04:58 2001
@@ -2665,6 +2665,8 @@
}
return 0;

+ case BLKGETLASTSECT:
+ case BLKSETLASTSECT:
case BLKROSET:
case BLKROGET:
case BLKFLSBUF:
diff -ruP linux/drivers/scsi/sd.c linux-ioctl/drivers/scsi/sd.c
--- linux/drivers/scsi/sd.c Fri Oct 27 01:35:48 2000
+++ linux-ioctl/drivers/scsi/sd.c Tue Feb 13 11:05:25 2001
@@ -225,6 +225,8 @@
return -EINVAL;
return put_user(sd[SD_PARTITION(inode->i_rdev)].nr_sects, (long *) arg);

+ case BLKGETLASTSECT:
+ case BLKSETLASTSECT:
case BLKROSET:
case BLKROGET:
case BLKRASET:
diff -ruP linux/include/linux/fs.h linux-ioctl/include/linux/fs.h
--- linux/include/linux/fs.h Tue Jan 30 01:24:56 2001
+++ linux-ioctl/include/linux/fs.h Tue Feb 13 11:08:43 2001
@@ -180,6 +180,8 @@
/* This was here just to show that the number is taken -
probably all these _IO(0x12,*) ioctls should be moved to blkpg.h. */
#endif
+#define BLKGETLASTSECT _IO(0x12,108) /* get last sector of block device */
+#define BLKSETLASTSECT _IO(0x12,109) /* get last sector of block device */


#define BMAP_IOCTL 1 /* obsolete - kept for compatibility */
\
 
 \ /
  Last update: 2005-03-22 13:14    [W:0.036 / U:0.764 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site