problems getting last page of a file written to disk properly in a filter driver

Hi,

I’m working on a filesystem filter driver which does encryption of
selected files. My basic strategy is as follows: Intercept creates,
determine if the file is one I would like to encrypt, if so, add
FILE_WRITE_THROUGH to the create flags and save the
PtrFileObject->FsContext in a list so I can identify filtered fileobjects
for reads/writes. Intercept writes, if cached write, do not filter it, let
it go though, a noncached write will follow - encrypt this noncached data
in a nonpagedpool allocated buffer and replace the irp’s data buffer with
this encrypted data buffer, and then in the completion routine, replace
the encrypted buffer back with the original one. Intercept reads, if
cached read, let it complete normally, since data in the cache is not
encrypted. If non-cached read (IRP_NOCACHE set), decrypt the data since we
know it is encrypted on disk. This strategy works GREAT in almost all
cases, but unfortunately, one specific case causes problems.
In NTFS, if I have an encrypted file and I open it in a text editor (and
it becomes decrypted through the filter so it looks like normal text),
then I delete the last few bytes of the file and save it, then the last
page of the file gets written to disk unencrypted. This is especially
strange because I actually get the noncached write through write for the
last page, and it gets sent to the fsd, but apparently, the fsd is
ignoring it or something. So for example, my file is 9K bytes, encrypted
on disk, I open it in notepad, delete the last byte of the file, so now
it’s 8191 bytes. I get a cached write at offset 0 for 8191 bytes, I let it
go through, then I get a noncached write for 8192 bytes (noncached writes
are rounded to sector lengths by the cache manager) at offset 0, I encrypt
the data as described and then let it go through. Somehow the bytes from
0-4095 are written encrypted to disk, but the bytes from 4096 to 8191 are
written unencrypted. This is true for any size file, and it is always only
the last page (which is actually < 4K) that is unencrypted on disk. It is also not the case if instead of deleting bytes I add bytes, I get the exact same write irps, but all data is written encrypted to disk properly. I thought it might be that the fsd is somehow figuring out that the last page is not dirty so it doesn’t actually send the write through, but I tried deleting bytes and modifying some bytes before then, but still in the last page, and it still gets written unencrypted to disk. Any and all help would be appreciated.

Thanks,
Mark

Comments in line.

----- Original Message -----
From: “Mark”
To: “File Systems Developers”
Sent: Tuesday, August 27, 2002 4:31 PM
Subject: [ntfsd] problems getting last page of a file written to disk
properly in a filter driver

> Hi,
>
> I’m working on a filesystem filter driver which does encryption of
> selected files. My basic strategy is as follows: Intercept creates,
> determine if the file is one I would like to encrypt, if so, add
> FILE_WRITE_THROUGH to the create flags and save the
> PtrFileObject->FsContext in a list so I can identify filtered fileobjects
> for reads/writes.
FsContext is not valid until your completion routine. I also do not
recommend adding any flags since the caller will not be expecting them. Try
using WordPad, NotePad, and the Office programs and see if that causes
disruptions. If you can handle all of the Microsoft programs, you stand a
very good chance of handling anything.

> Intercept writes, if cached write, do not filter it, let
> it go though, a noncached write will follow - encrypt this noncached data
> in a nonpagedpool allocated buffer and replace the irp’s data buffer with
> this encrypted data buffer, and then in the completion routine, replace
> the encrypted buffer back with the original one. Intercept reads, if
> cached read, let it complete normally, since data in the cache is not
> encrypted. If non-cached read (IRP_NOCACHE set), decrypt the data since we
> know it is encrypted on disk. This strategy works GREAT in almost all
> cases, but unfortunately, one specific case causes problems.
> In NTFS, if I have an encrypted file and I open it in a text editor (and
> it becomes decrypted through the filter so it looks like normal text),
> then I delete the last few bytes of the file and save it, then the last
> page of the file gets written to disk unencrypted. This is especially
> strange because I actually get the noncached write through write for the
> last page, and it gets sent to the fsd, but apparently, the fsd is
> ignoring it or something. So for example, my file is 9K bytes, encrypted
> on disk, I open it in notepad, delete the last byte of the file, so now
> it’s 8191 bytes. I get a cached write at offset 0 for 8191 bytes, I let it
> go through, then I get a noncached write for 8192 bytes (noncached writes
> are rounded to sector lengths by the cache manager) at offset 0, I encrypt
> the data as described and then let it go through. Somehow the bytes from
> 0-4095 are written encrypted to disk, but the bytes from 4096 to 8191 are
> written unencrypted. This is true for any size file, and it is always only
> the last page (which is actually < 4K) that is unencrypted on disk.
> It is also not the case if instead of deleting bytes I add bytes, I get
the
> exact same write irps, but all data is written encrypted to disk properly.
> I thought it might be that the fsd is somehow figuring out that the last
page
> is not dirty so it doesn’t actually send the write through, but I tried
deleting
> bytes and modifying some bytes before then, but still in the last page,
and
> it still gets written unencrypted to disk. Any and all help would be
appreciated.
>
You have to be careful with looking for requests to change the size of a
file. Correctly applying those changes is very difficult as many have come
to learn. It is even more difficult if the file contains a header or
trailer for your filter’s exclusive use that is not given to the opening
application. Your explanation leads me to think that you do not decrypt on
reads flagged with the cached bit(s) set. That is wrong. It is not
possible to have encrypted data in the cache. If you attempt to do that,
some applications will fail. There is a request an app can issue that
allows it to have access to the cache manager’s buffers for that file. That
is direct read/write access and the file systems, including filters, do not
see those accesses. I hope ‘noncached’ writes mean those with the nocache
bit set that are never put in the cache manager’s buffers. Those are a pain
and will take time to get them right. You need the correct file size to
handle the final block that will be written. While it might be a sector or
cluster sized write, you will have to only touch the part of the buffer
where valid resides.

Thanks for your response. I wasn’t clear about the FsContext. I am indeed
saving this off in the create completion routine. As for adding the
FILE_WRITE_THROUGH flag, this seems to work with all the microsoft
applications I’ve tried (as opposed to FILE_NO_INTERMEDIATE_BUFFERING)
which fails under wordpad, etc.
All data in my cache is unencrypted. I only encrypt/decrypt on writes
to/from disk (respectively). I use the IRP_NOCACHE flag to tell me whether
the current read/write is to/from disk (as opposed to to/from cache). If
IRP_NOCACHE is set, I encrypt/decrypt, otherwise, I leave it alone. This
ensures that all data in cache is unencrypted.
As for your comment about being careful about requests that change the
size of the file, could you elaborate on this more? I can only modify the
bytes in the range of the valid filesize even though the cache buffer I
get extends beyond the end of the file? Why is this? What happens if I do?

Thanks again,
Mark

Comments in line.

----- Original Message -----
From: “Mark”
> To: “File Systems Developers”
> Sent: Tuesday, August 27, 2002 4:31 PM
> Subject: [ntfsd] problems getting last page of a file written to disk
> properly in a filter driver
>
>
> > Hi,
> >
> > I’m working on a filesystem filter driver which does encryption of
> > selected files. My basic strategy is as follows: Intercept creates,
> > determine if the file is one I would like to encrypt, if so, add
> > FILE_WRITE_THROUGH to the create flags and save the
> > PtrFileObject->FsContext in a list so I can identify filtered fileobjects
> > for reads/writes.
> FsContext is not valid until your completion routine. I also do not
> recommend adding any flags since the caller will not be expecting them. Try
> using WordPad, NotePad, and the Office programs and see if that causes
> disruptions. If you can handle all of the Microsoft programs, you stand a
> very good chance of handling anything.
>
> > Intercept writes, if cached write, do not filter it, let
> > it go though, a noncached write will follow - encrypt this noncached data
> > in a nonpagedpool allocated buffer and replace the irp’s data buffer with
> > this encrypted data buffer, and then in the completion routine, replace
> > the encrypted buffer back with the original one. Intercept reads, if
> > cached read, let it complete normally, since data in the cache is not
> > encrypted. If non-cached read (IRP_NOCACHE set), decrypt the data since we
> > know it is encrypted on disk. This strategy works GREAT in almost all
> > cases, but unfortunately, one specific case causes problems.
> > In NTFS, if I have an encrypted file and I open it in a text editor (and
> > it becomes decrypted through the filter so it looks like normal text),
> > then I delete the last few bytes of the file and save it, then the last
> > page of the file gets written to disk unencrypted. This is especially
> > strange because I actually get the noncached write through write for the
> > last page, and it gets sent to the fsd, but apparently, the fsd is
> > ignoring it or something. So for example, my file is 9K bytes, encrypted
> > on disk, I open it in notepad, delete the last byte of the file, so now
> > it’s 8191 bytes. I get a cached write at offset 0 for 8191 bytes, I let it
> > go through, then I get a noncached write for 8192 bytes (noncached writes
> > are rounded to sector lengths by the cache manager) at offset 0, I encrypt
> > the data as described and then let it go through. Somehow the bytes from
> > 0-4095 are written encrypted to disk, but the bytes from 4096 to 8191 are
> > written unencrypted. This is true for any size file, and it is always only
> > the last page (which is actually < 4K) that is unencrypted on disk.
> > It is also not the case if instead of deleting bytes I add bytes, I get
> the
> > exact same write irps, but all data is written encrypted to disk properly.
> > I thought it might be that the fsd is somehow figuring out that the last
> page
> > is not dirty so it doesn’t actually send the write through, but I tried
> deleting
> > bytes and modifying some bytes before then, but still in the last page,
> and
> > it still gets written unencrypted to disk. Any and all help would be
> appreciated.
> >
> You have to be careful with looking for requests to change the size of a
> file. Correctly applying those changes is very difficult as many have come
> to learn. It is even more difficult if the file contains a header or
> trailer for your filter’s exclusive use that is not given to the opening
> application. Your explanation leads me to think that you do not decrypt on
> reads flagged with the cached bit(s) set. That is wrong. It is not
> possible to have encrypted data in the cache. If you attempt to do that,
> some applications will fail. There is a request an app can issue that
> allows it to have access to the cache manager’s buffers for that file. That
> is direct read/write access and the file systems, including filters, do not
> see those accesses. I hope ‘noncached’ writes mean those with the nocache
> bit set that are never put in the cache manager’s buffers. Those are a pain
> and will take time to get them right. You need the correct file size to
> handle the final block that will be written. While it might be a sector or
> cluster sized write, you will have to only touch the part of the buffer
> where valid resides.

IRP_NOCACHE, IRP_PAGING_IO, and IRP_SYNCHRONOUS_PAGING_IO all have an impact
on encryption filters. Why use FILE_WRITE_THROUGH if the cache is
plaintext? If you see a normal write without the IRP_NOCACHE bit set, just
let the FSD pass it off to the cache manager. It will come in again with
the IRP_?_PAGING_IO bit(s) set and encryption can be done then.

If this work is being done for your employer, I would suggest you buy the
file system filter kit from OSR. I am pretty sure that your company has a
source code license to Windows, so you can get the source for the OSR kit.
Getting the file system kit would help too, but it is not necessary. Peter,
how do you like the free plug and I didn’t get any bacon wrapped scallops.
Nothing requires that a FSD actually transfers any data beyond the known EOF
into any buffer you, as a file system filter, can see. I wouldn’t depend
upon it.

Please read all the OSR articles on file system filters. Also, remember
that if you’re targeting XP, there is a much better solution to tracking
FsContexts and FileObjects. Encryption is a very difficult thing to
implement on Windows NT/2000/XP. Adding network file systems will be
another big headache. I would love a good encryption solution for me
personally, but even though I have written them, when employed, I won’t try
one for fun - even if I can control the use and limit the programs that will
be accessing the encrypted file. I don’t trust the currently released
solutions because I don’t trust them. I don’t know what backdoors are
hidden and if the encryption algorithm is any good.

P.S. If you would like to continue this off line, let me know by responding
to the email address given. I will give out a better address later.

P.P.S. Just because I am paranoid, it doesn’t mean that the whole world is
not out to get me!

----- Original Message -----
From: “Mark”
To: “File Systems Developers”
Sent: Tuesday, August 27, 2002 6:21 PM
Subject: [ntfsd] Re: problems getting last page of a file written to disk
properly in a filter driver

> Thanks for your response. I wasn’t clear about the FsContext. I am indeed
> saving this off in the create completion routine. As for adding the
> FILE_WRITE_THROUGH flag, this seems to work with all the microsoft
> applications I’ve tried (as opposed to FILE_NO_INTERMEDIATE_BUFFERING)
> which fails under wordpad, etc.
> All data in my cache is unencrypted. I only encrypt/decrypt on writes
> to/from disk (respectively). I use the IRP_NOCACHE flag to tell me whether
> the current read/write is to/from disk (as opposed to to/from cache). If
> IRP_NOCACHE is set, I encrypt/decrypt, otherwise, I leave it alone. This
> ensures that all data in cache is unencrypted.
> As for your comment about being careful about requests that change the
> size of the file, could you elaborate on this more? I can only modify the
> bytes in the range of the valid filesize even though the cache buffer I
> get extends beyond the end of the file? Why is this? What happens if I do?
>
> Thanks again,
> Mark
>
> > Comments in line.
> >
> > ----- Original Message -----
> > From: “Mark”
> > To: “File Systems Developers”
> > Sent: Tuesday, August 27, 2002 4:31 PM
> > Subject: [ntfsd] problems getting last page of a file written to disk
> > properly in a filter driver
> >
> >
> > > Hi,
> > >
> > > I’m working on a filesystem filter driver which does encryption of
> > > selected files. My basic strategy is as follows: Intercept creates,
> > > determine if the file is one I would like to encrypt, if so, add
> > > FILE_WRITE_THROUGH to the create flags and save the
> > > PtrFileObject->FsContext in a list so I can identify filtered
fileobjects
> > > for reads/writes.
> > FsContext is not valid until your completion routine. I also do not
> > recommend adding any flags since the caller will not be expecting them.
Try
> > using WordPad, NotePad, and the Office programs and see if that causes
> > disruptions. If you can handle all of the Microsoft programs, you stand
a
> > very good chance of handling anything.
> >
> > > Intercept writes, if cached write, do not filter it, let
> > > it go though, a noncached write will follow - encrypt this noncached
data
> > > in a nonpagedpool allocated buffer and replace the irp’s data buffer
with
> > > this encrypted data buffer, and then in the completion routine,
replace
> > > the encrypted buffer back with the original one. Intercept reads, if
> > > cached read, let it complete normally, since data in the cache is not
> > > encrypted. If non-cached read (IRP_NOCACHE set), decrypt the data
since we
> > > know it is encrypted on disk. This strategy works GREAT in almost all
> > > cases, but unfortunately, one specific case causes problems.
> > > In NTFS, if I have an encrypted file and I open it in a text editor
(and
> > > it becomes decrypted through the filter so it looks like normal text),
> > > then I delete the last few bytes of the file and save it, then the
last
> > > page of the file gets written to disk unencrypted. This is especially
> > > strange because I actually get the noncached write through write for
the
> > > last page, and it gets sent to the fsd, but apparently, the fsd is
> > > ignoring it or something. So for example, my file is 9K bytes,
encrypted
> > > on disk, I open it in notepad, delete the last byte of the file, so
now
> > > it’s 8191 bytes. I get a cached write at offset 0 for 8191 bytes, I
let it
> > > go through, then I get a noncached write for 8192 bytes (noncached
writes
> > > are rounded to sector lengths by the cache manager) at offset 0, I
encrypt
> > > the data as described and then let it go through. Somehow the bytes
from
> > > 0-4095 are written encrypted to disk, but the bytes from 4096 to 8191
are
> > > written unencrypted. This is true for any size file, and it is always
only
> > > the last page (which is actually < 4K) that is unencrypted on disk.
> > > It is also not the case if instead of deleting bytes I add bytes, I
get
> > the
> > > exact same write irps, but all data is written encrypted to disk
properly.
> > > I thought it might be that the fsd is somehow figuring out that the
last
> > page
> > > is not dirty so it doesn’t actually send the write through, but I
tried
> > deleting
> > > bytes and modifying some bytes before then, but still in the last
page,
> > and
> > > it still gets written unencrypted to disk. Any and all help would be
> > appreciated.
> > >
> > You have to be careful with looking for requests to change the size of a
> > file. Correctly applying those changes is very difficult as many have
come
> > to learn. It is even more difficult if the file contains a header or
> > trailer for your filter’s exclusive use that is not given to the opening
> > application. Your explanation leads me to think that you do not decrypt
on
> > reads flagged with the cached bit(s) set. That is wrong. It is not
> > possible to have encrypted data in the cache. If you attempt to do
that,
> > some applications will fail. There is a request an app can issue that
> > allows it to have access to the cache manager’s buffers for that file.
That
> > is direct read/write access and the file systems, including filters, do
not
> > see those accesses. I hope ‘noncached’ writes mean those with the
nocache
> > bit set that are never put in the cache manager’s buffers. Those are a
pain
> > and will take time to get them right. You need the correct file size to
> > handle the final block that will be written. While it might be a sector
or
> > cluster sized write, you will have to only touch the part of the buffer
> > where valid resides.
>
> —
> You are currently subscribed to ntfsd as: xxxxx@yoshimuni.com
> To unsubscribe send a blank email to %%email.unsub%%