As part of the ntfsimage I'm writing, I want to be able to call some
of the functions in libntfs that normally write to the disk, but
without actually modifying the partition; instead, I want to be able
to divert the `writes' to my own code to write the image. (See [1]
below for more explanation of this.)
At the moment, disk_io's ntfs_pread et al all read and write directly
using vol->fd. Without writing a kernel device driver, that makes it
difficult to virtualise. I expect there are other reasons you might
want to virtualise libntfs's disk I/O.
So I wrote the patch below. It is against linux-ntfs 1.7.1 and makes
the following changes:
* ntfs_volume contains a new member which if non-null points to
a struct describing how to do disk I/O. I put the new member at the
end to make it ABI-compatible. Regrettably this makes mkntfs break
when old binaries are run agains the new library because mkntfs uses
calloc directly to allocate an ntfs_volume rather than calling
__ntfs_volume_allocate.
Also, I assume that calloc() results in zero pointers; this is
strictly speaking not a safe assumption according to C89. If you
think this is a problem (ie if you don't already make this
assumption somewhere) then __ntfs_volume_allocate should clear the
new member.
* ntfs_pread and pwrite are split into two functions each:
ntfs_pread/pwrite_vol are used everywhere, and take a volume pointer
instead of an fd. They check the virtual I/O pointer, and if
non-null just use that. Otherwise they call ntfs_pread/pwrite_fd,
which are just renamed versions of ntfs_pread/pwrite. (I renamed
the direct functions so that I would catch every caller.)
* All callers of ntfs_pread/pwrite are changed to call
ntfs_pread/pwrite_vol. This means that ntfs_mst_pread/pwrite need
to take a volume pointer instead of an fd, but this is luckily not a
problem.
* ntfs_*_pread erroneously declared their output buffer as
const void* rather than just void*. This is fixed.
[1] Why do I want to do virtualised I/O ? Because I want to be able
to mark a filesystem as dirty in ntfsimage's output without modifying
the underlying disk, as I've previously explained. This is most
easily accomplished by using libntfs's existing function for setting
the relevant flags. The alternative would be to replicate much of
that machinery.
Thanks,
Ian.
diff -ru orig/linux-ntfs-1.7.1/include/disk_io.h linux-ntfs-1.7.1/include/disk_io.h
--- orig/linux-ntfs-1.7.1/include/disk_io.h 2003-02-06 15:24:56.000000000 +0000
+++ linux-ntfs-1.7.1/include/disk_io.h 2003-05-03 13:51:25.000000000 +0100
@@ -24,16 +24,23 @@
#include "volume.h"
-extern s64 ntfs_pread(const int fd, const s64 pos, s64 count, const void *b);
-extern s64 ntfs_pwrite(const int fd, const s64 pos, s64 count, const void *b);
-
-extern s64 ntfs_mst_pread(const int fd, const s64 pos, s64 count,
- const u32 bksize, const void *b);
-extern s64 ntfs_mst_pwrite(const int fd, const s64 pos, s64 count,
+extern s64 ntfs_pread_fd(const int fd, const s64 pos,
+ s64 count, void *b);
+extern s64 ntfs_pwrite_fd(const int fd, const s64 pos,
+ s64 count, const void *b);
+
+extern s64 ntfs_pread_vol(const ntfs_volume *vol, const s64 pos,
+ s64 count, void *b);
+extern s64 ntfs_pwrite_vol(const ntfs_volume *vol, const s64 pos,
+ s64 count, const void *b);
+
+extern s64 ntfs_mst_pread(const ntfs_volume *vol, const s64 pos, s64 count,
+ const u32 bksize, void *b);
+extern s64 ntfs_mst_pwrite(const ntfs_volume *vol, const s64 pos, s64 count,
const u32 bksize, const void *b);
extern s64 ntfs_cluster_read(const ntfs_volume *vol, const s64 lcn,
- const s64 count, const void *b);
+ const s64 count, void *b);
extern s64 ntfs_cluster_write(const ntfs_volume *vol, const s64 lcn,
const s64 count, const void *b);
diff -ru orig/linux-ntfs-1.7.1/include/volume.h linux-ntfs-1.7.1/include/volume.h
--- orig/linux-ntfs-1.7.1/include/volume.h 2003-02-06 15:24:56.000000000 +0000
+++ linux-ntfs-1.7.1/include/volume.h 2003-05-03 13:43:10.000000000 +0100
@@ -82,6 +82,14 @@
#define NTFS_BUF_SIZE 8192
+typedef struct _ntfs_readwrite_virtual {
+ s64 (*read_method)(struct _ntfs_readwrite_virtual *object,
+ const s64 pos, s64 count, void *b);
+ s64 (*write_method)(struct _ntfs_readwrite_virtual *object,
+ const s64 pos, s64 count, const void *b);
+ /* method-specific data may follow here */
+} ntfs_readwrite_virtual;
+
/*
* ntfs_volume - structure describing an open volume in memory
*/
@@ -152,6 +160,8 @@
FILE_AttrDef. */
u32 attrdef_len; /* Size of the attribute definition table in
bytes. */
+ ntfs_readwrite_virtual *rw_virtual;/* 0 to use ntfs_pread/write_fd.
+ Otherwise is the object to use for I/O */
};
extern ntfs_volume *ntfs_volume_startup(const char *name, unsigned long rwflag);
diff -ru orig/linux-ntfs-1.7.1/libntfs/attrib.c linux-ntfs-1.7.1/libntfs/attrib.c
--- orig/linux-ntfs-1.7.1/libntfs/attrib.c 2003-02-13 10:25:30.000000000 +0000
+++ linux-ntfs-1.7.1/libntfs/attrib.c 2003-05-03 13:49:17.000000000 +0100
@@ -150,7 +150,7 @@
* or can we have sparse runs in uncompressed
* files as well?
*/
- r = ntfs_pread(vol->fd, rl[i].lcn <<
+ r = ntfs_pread_vol(vol, rl[i].lcn <<
vol->cluster_size_bits,
rl[i].length <<
vol->cluster_size_bits, intbuf);
@@ -188,7 +188,7 @@
* to 0 for the length of the run, which should
* be 16 (= compression unit size).
*/
- r = ntfs_pread(vol->fd, rl[i].lcn <<
+ r = ntfs_pread_vol(vol, rl[i].lcn <<
vol->cluster_size_bits,
rl[i].length <<
vol->cluster_size_bits,
@@ -698,7 +698,8 @@
Dprintf("%s(): Reading 0x%Lx bytes from vcn 0x%Lx, lcn 0x%Lx, "
"ofs 0x%Lx.\n", __FUNCTION__, to_read,
rl->vcn, rl->lcn, ofs);
- br = ntfs_pread(f, (rl->lcn << vol->cluster_size_bits) + ofs,
+ br = ntfs_pread_vol(vol,
+ (rl->lcn << vol->cluster_size_bits) + ofs,
to_read, b);
/* If everything ok, update progress counters and continue. */
if (br > 0) {
@@ -961,7 +962,7 @@
"ofs 0x%Lx.\n", __FUNCTION__, to_write,
rl->vcn, rl->lcn, ofs);
if (!NVolReadOnly(vol))
- written = ntfs_pwrite(f, (rl->lcn <<
+ written = ntfs_pwrite_vol(vol, (rl->lcn <<
vol->cluster_size_bits) + ofs,
to_write, b);
else
diff -ru orig/linux-ntfs-1.7.1/libntfs/disk_io.c linux-ntfs-1.7.1/libntfs/disk_io.c
--- orig/linux-ntfs-1.7.1/libntfs/disk_io.c 2003-02-06 15:24:56.000000000 +0000
+++ linux-ntfs-1.7.1/libntfs/disk_io.c 2003-05-03 13:52:43.000000000 +0100
@@ -41,14 +41,14 @@
#endif
/**
- * ntfs_pread - positioned read from disk
- * @fd: file descriptor to read from
+ * ntfs_pread_vol - positioned read from volume
+ * @vol: volume to read from
* @pos: position in file descriptor to read from
* @count: number of bytes to read
* @b: output data buffer
*
- * This function will read @count bytes from file descriptor @fd at position
- * @pos into the data buffer @b.
+ * This function will read @count bytes from the device containing
+ * volume @vol at position @pos into the data buffer @b.
*
* On success, return the number of successfully read bytes. If this number is
* lower than @count this means that we have either reached end of file or
@@ -59,7 +59,27 @@
* to the return code of either lseek, read, or set to EINVAL in case of
* invalid arguments.
*/
-s64 ntfs_pread(const int fd, const s64 pos, s64 count, const void *b)
+s64 ntfs_pread_vol(const ntfs_volume *vol, const s64 pos,
+ s64 count, void *b)
+{
+ if (vol->rw_virtual)
+ return vol->rw_virtual->read_method(vol->rw_virtual, pos, count, b);
+ else
+ return ntfs_pread_fd(vol->fd, pos, count, b);
+}
+
+/**
+ * ntfs_pread_fd - positioned read from disk, directly to fd
+ * @fd: file descriptor to read from
+ * @pos: position in file descriptor to read from
+ * @count: number of bytes to read
+ * @b: output data buffer
+ *
+ * This function will read @count bytes from file descriptor @fd at position
+ * @pos into the data buffer @b. It is similar to ntfs_pread_vol, but
+ * does not support the ntfs_readwrite_virtual so should not usually be used.
+ */
+s64 ntfs_pread_fd(const int fd, const s64 pos, s64 count, void *b)
{
s64 br, total;
@@ -94,14 +114,14 @@
}
/**
- * ntfs_pwrite - positioned write to disk
- * @fd: file descriptor to write to
+ * ntfs_pwrite_vol - positioned write to disk
+ * @vol: volume to write to
* @pos: position in file descriptor to write to
* @count: number of bytes to write
* @b: data buffer to write to disk
*
- * This function will write @count bytes from data buffer @b to file descriptor
- * @fd at position @pos.
+ * This function will write @count bytes from data buffer @b to
+ * the device containing volume @vol at position @pos.
*
* On success, return the number of successfully written bytes. If this number
* is lower than @count this means that the write has been interrupted in
@@ -112,7 +132,27 @@
* appropriately to the return code of either lseek, write, or set
* to EINVAL in case of invalid arguments.
*/
-s64 ntfs_pwrite(const int fd, const s64 pos, s64 count, const void *b)
+s64 ntfs_pwrite_vol(const ntfs_volume *vol, const s64 pos,
+ s64 count, const void *b)
+{
+ if (vol->rw_virtual)
+ return vol->rw_virtual->write_method(vol->rw_virtual, pos, count, b);
+ else
+ return ntfs_pwrite_fd(vol->fd, pos, count, b);
+}
+
+/**
+ * ntfs_pwrite_fd - positioned write to disk, directly to fd
+ * @fd: file descriptor to write to
+ * @pos: position in file descriptor to write to
+ * @count: number of bytes to write
+ * @b: data buffer to write to disk
+ *
+ * This function will write @count bytes from data buffer @b to file descriptor
+ * @fd at position @pos. It is similar to ntfs_pwrite_vol, but
+ * does not support the ntfs_readwrite_virtual so should not usually be used.
+ */
+s64 ntfs_pwrite_fd(const int fd, const s64 pos, s64 count, const void *b)
{
s64 written, total;
@@ -177,8 +217,8 @@
* sector transfer error. This should be detected by the caller by checking for
* the magic being "BAAD".
*/
-s64 ntfs_mst_pread(const int fd, const s64 pos, s64 count,
- const u32 bksize, const void *b)
+s64 ntfs_mst_pread(const ntfs_volume *vol, const s64 pos, s64 count,
+ const u32 bksize, void *b)
{
s64 br, i;
@@ -187,7 +227,7 @@
return -1;
}
/* Do the read. */
- br = ntfs_pread(fd, pos, count * bksize, b);
+ br = ntfs_pread_vol(vol, pos, count * bksize, b);
if (br < 0)
return br;
/*
@@ -234,7 +274,7 @@
* simulating an mst read on the written data. This way cache coherency is
* achieved.
*/
-s64 ntfs_mst_pwrite(const int fd, const s64 pos, s64 count,
+s64 ntfs_mst_pwrite(const ntfs_volume *vol, const s64 pos, s64 count,
const u32 bksize, const void *b)
{
s64 written, i;
@@ -260,7 +300,7 @@
}
}
/* Write the prepared data. */
- written = ntfs_pwrite(fd, pos, count * bksize, b);
+ written = ntfs_pwrite_vol(vol, pos, count * bksize, b);
/* Quickly deprotect the data again. */
for (i = 0; i < count; ++i)
ntfs_mst_post_write_fixup((NTFS_RECORD*)((u8*)b + i * bksize));
@@ -282,7 +322,7 @@
* with errno set to the error code.
*/
s64 ntfs_cluster_read(const ntfs_volume *vol, const s64 lcn,
- const s64 count, const void *b)
+ const s64 count, void *b)
{
s64 br;
@@ -294,7 +334,7 @@
errno = ESPIPE;
return -1;
}
- br = ntfs_pread(vol->fd, lcn << vol->cluster_size_bits,
+ br = ntfs_pread_vol(vol, lcn << vol->cluster_size_bits,
count << vol->cluster_size_bits, b);
if (br < 0) {
Dperror("Error reading cluster(s)");
@@ -328,7 +368,7 @@
return -1;
}
if (!NVolReadOnly(vol))
- bw = ntfs_pwrite(vol->fd, lcn << vol->cluster_size_bits,
+ bw = ntfs_pwrite_vol(vol, lcn << vol->cluster_size_bits,
count << vol->cluster_size_bits, b);
else
bw = count << vol->cluster_size_bits;
diff -ru orig/linux-ntfs-1.7.1/libntfs/runlist.c linux-ntfs-1.7.1/libntfs/runlist.c
--- orig/linux-ntfs-1.7.1/libntfs/runlist.c 2003-02-06 15:24:56.000000000 +0000
+++ linux-ntfs-1.7.1/libntfs/runlist.c 2003-05-03 13:50:08.000000000 +0100
@@ -1075,7 +1075,7 @@
ofs);
retry:
if (!NVolReadOnly(vol))
- written = ntfs_pwrite(f, (rl->lcn <<
+ written = ntfs_pwrite_vol(vol, (rl->lcn <<
vol->cluster_size_bits) + ofs,
to_write, b);
else
diff -ru orig/linux-ntfs-1.7.1/libntfs/volume.c linux-ntfs-1.7.1/libntfs/volume.c
--- orig/linux-ntfs-1.7.1/libntfs/volume.c 2003-02-06 15:24:56.000000000 +0000
+++ linux-ntfs-1.7.1/libntfs/volume.c 2003-05-03 13:50:27.000000000 +0100
@@ -118,7 +118,7 @@
vol->mft_ni->mft_no = 0;
vol->mft_ni->mrec = mb;
/* Can't use any of the higher level functions yet! */
- l = ntfs_mst_pread(vol->fd, vol->mft_lcn << vol->cluster_size_bits, 1,
+ l = ntfs_mst_pread(vol, vol->mft_lcn << vol->cluster_size_bits, 1,
vol->mft_record_size, mb);
if (l != 1) {
if (l != -1)
@@ -407,7 +407,7 @@
goto error_exit;
}
/* Now read the bootsector. */
- br = ntfs_pread(vol->fd, 0, sizeof(NTFS_BOOT_SECTOR), bs);
+ br = ntfs_pread_vol(vol, 0, sizeof(NTFS_BOOT_SECTOR), bs);
if (br != sizeof(NTFS_BOOT_SECTOR)) {
Dputs(FAILED);
if (br != -1)
diff -ru orig/linux-ntfs-1.7.1/ntfsprogs/mkntfs.c linux-ntfs-1.7.1/ntfsprogs/mkntfs.c
--- orig/linux-ntfs-1.7.1/ntfsprogs/mkntfs.c 2003-02-10 12:00:47.000000000 +0000
+++ linux-ntfs-1.7.1/ntfsprogs/mkntfs.c 2003-05-03 13:52:07.000000000 +0100
@@ -3504,7 +3504,7 @@
lw = 1;
for (i = 0; i < opts.mft_size / vol->mft_record_size; i++) {
if (!opts.no_action)
- lw = ntfs_mst_pwrite(vol->fd, pos, 1,
+ lw = ntfs_mst_pwrite(vol, pos, 1,
vol->mft_record_size,
buf + i * vol->mft_record_size);
if (lw != 1)
@@ -3531,7 +3531,7 @@
usn = 0xfffe;
*usnp = cpu_to_le16(usn);
if (!opts.no_action)
- lw = ntfs_mst_pwrite(vol->fd, pos, 1,
+ lw = ntfs_mst_pwrite(vol, pos, 1,
vol->mft_record_size,
buf + i * vol->mft_record_size);
if (lw != 1)
|