Re: VFS write operations (Michael Welch)
Re: VFS write operations (Michael Welch)
- Subject: Re: VFS write operations (Michael Welch)
- From: Sam Vaughan <email@hidden>
- Date: Wed, 13 Sep 2006 12:06:00 +1000
Hi Mike,
On 13/09/2006, at 5:03 AM, Michael Welch <email@hidden> wrote:
From the terminal, I can read (e.g. $cat filename), write $echo
"foo" >
filename), and append ($echo "foo" >> filename). All seems well.
Good, this means that your VNOP_READ and VNOP_WRITE code is working.
Saving from TextEdit will need more than that though...
Then I try to write from a simple app like TextEdit... here's where
the
fun begins. I open a file in TextEdit (it can read just fine), make
some changes, and hit save. From what I can decipher, TextEdit
performs
the following steps (with the corresponding VNOP's going through my
VFS):
1) Open the existing file, read a whole slew of attributes
(permissions,
file size, timestamps, etc.), and then close the file.
2) Repeat (1) for the parent dir, it's parent, etc. up to the root of
the file system.
3) Create a temporary file, open it, and write the data.
4) Read some attributes on the temp file (only uid, gid, flags,
fileid,
and fsid) and close it.
5) Delete the temp file.
6) Report that the file could not be saved (duh - you just deleted
it!).
My best (only) guess is that it's unhappy with the attributes it
sees -
but what I return in those attributes are the same values specified
during file creation time (except size, which is updated at write
time).
Stepping through I've verified that the temp file is created in the
same
directory as my cache file and the data is in fact written to it after
step 3. The attributes look right.
So writing a file from TextEdit fails. However - if the file size is
zero, the save succeeds (that is, if the new size is zero,
regardless of
what the old size was). Instead of (4) and (5), it does what you'd
pretty much expect - delete the original file and rename the temp file
back into place.
TextEdit is trying to do a safe save. i.e. write to a different file
and swap that file with the original file in a file-system-atomic
way. It'll try to use exchangedata(2) first, and if that fails,
it'll use two rename(2) operations.
Have you implemented VNOP_EXCHANGE? If so then that's probably where
you should be looking. Otherwise I'd say the issue will be in your
VNOP_RENAME. The ktrace(1) tool is going to be your best bet to
narrow down your problem.
I'd recommend walking through the ktrace output from successful
TextEdit saves and comparing them to the ktrace output from a save to
your file system. You might want to repeat this on a UFS formatted
disk image to see the generic file system behaviour when things like
VolFS and exchangedata aren't involved.
So on HFS+:
- - - - - - - -
ti:~ sjv: echo foo > file.txt
ti:~ sjv: open !$
open file.txt
ti:~ sjv: ktrace -p `ps auxww | grep [T]extEdit | awk '{print
$2;}'`
<modify and save the file in TextEdit>
ti:~ sjv: ktrace -C
ti:~ sjv: kdump
<snip uid, DNLC, stat, attr and access stuff>
-- check availability of temp file name A ".15066-179801665-1.txt":
15066 TextEdit CALL lstat(0xbfffd4c0,0xbfffd930)
15066 TextEdit NAMI "/Users/sjv/.15066-179801665-1.txt"
15066 TextEdit RET lstat -1 errno 2 No such file or directory
<snip umask stuff>
15066 TextEdit CALL stat(0xbfffdda0,0xbfffd500)
15066 TextEdit NAMI "/Users/sjv/.15066-179801665-1.txt"
15066 TextEdit RET stat -1 errno 2 No such file or directory
-- write data to temp file B ".dat3ada.001":
15066 TextEdit CALL open(0xbfffd560,0xa02,0x180)
15066 TextEdit NAMI "/Users/sjv/.dat3ada.001"
15066 TextEdit RET open 12/0xc
15066 TextEdit CALL write(0xc,0x3837f0,0x5)
15066 TextEdit GIO fd 12 wrote 5 bytes
"food
"
15066 TextEdit RET write 5
15066 TextEdit CALL fsync(0xc)
15066 TextEdit RET fsync 0
15066 TextEdit CALL close(0xc)
15066 TextEdit RET close 0
-- rename B to A and chmod
15066 TextEdit CALL rename(0xbfffd560,0xbfffdda0)
15066 TextEdit NAMI "/Users/sjv/.dat3ada.001"
15066 TextEdit NAMI "/Users/sjv/.15066-179801665-1.txt"
15066 TextEdit RET rename 0
15066 TextEdit CALL chmod(0xbfffdda0,0x1a4)
15066 TextEdit NAMI "/Users/sjv/.15066-179801665-1.txt"
15066 TextEdit RET chmod 0
-- check for tilde file in case exchangedata isn't supported
15066 TextEdit CALL lstat(0xbfffd530,0xbfffd9a0)
15066 TextEdit NAMI "/Users/sjv/file~.txt"
15066 TextEdit RET lstat -1 errno 2 No such file or directory
-- exchangedata from A to original file
15066 TextEdit CALL exchangedata(0xbfffda10,0xbfffde20,0)
15066 TextEdit NAMI "/Users/sjv/.15066-179801665-1.txt"
15066 TextEdit NAMI "/Users/sjv/file.txt"
15066 TextEdit RET exchangedata 0
<snip stat stuff>
-- unlink temp file A
15066 TextEdit CALL unlink(0xbfffd3b0)
15066 TextEdit NAMI "/Users/sjv/.15066-179801665-1.txt"
15066 TextEdit RET unlink 0
<snip other foo>
- - - - - - - -
And on UFS, it's largely the same process with the addition of some
extra operations on Apple Double files, e.g. "._file.txt". There's
also a lot more stat/attr activity, something that clustered file
systems hate to see due to the metadata traffic it entails. (Radar
#3634554)
- - - - - - - -
ti:~ sjv: hdiutil create -fs UFS -size 10m -volname ufs ufs.dmg
> /dev/null
ti:~ sjv: hdiutil attach ufs.dmg
/dev/disk4 Apple_partition_scheme
/dev/disk4s1 Apple_partition_map
/dev/disk4s2 Apple_UFS /Volumes/
ufs
ti:~ sjv: echo foo > /Volumes/ufs/file.txt
ti:~ sjv: open !$
open /Volumes/ufs/file.txt
ti:~ sjv: ktrace -p `ps auxww | grep [T]extEdit | awk '{print
$2;}'`
ti:~ sjv: ktrace -C
ti:~ sjv: kdump
<snip uid, DNLC, stat, attr and access stuff>
<snip similar stuff to HFS+ above>
-- exchangedata fails because UFS doesn't support it:
15177 TextEdit CALL exchangedata(0xbfffda10,0xbfffde20,0)
15177 TextEdit NAMI "/Volumes/ufs/.15177-179803682-1.txt"
15177 TextEdit NAMI "/Volumes/ufs/file.txt"
15177 TextEdit RET exchangedata -1 errno 45 Operation not supported
<snip more stat/uid foo>
-- rename old files to tilde files
15177 TextEdit CALL rename(0xbfffc170,0xbfffc570)
15177 TextEdit NAMI "/Volumes/ufs/file.txt"
15177 TextEdit NAMI "/Volumes/ufs/file~.txt"
15177 TextEdit NAMI "._file.txt"
15177 TextEdit NAMI "._file~.txt"
15177 TextEdit RET rename 0
<snip uid foo>
-- check that the original directory entry is now available
15177 TextEdit CALL lstat(0xbfffcc50,0xbfffd0c0)
15177 TextEdit NAMI "/Volumes/ufs/file.txt"
15177 TextEdit RET lstat -1 errno 2 No such file or directory
<snip more stat/uid/attr foo>
-- better check again because we've been tooling around with stats
15177 TextEdit CALL lstat(0xbfffcc50,0xbfffd0c0)
15177 TextEdit NAMI "/Volumes/ufs/file.txt"
15177 TextEdit RET lstat -1 errno 2 No such file or directory
<snip uid foo>
-- rename our temporary file to the original file
15177 TextEdit CALL rename(0xbfffc170,0xbfffc570)
15177 TextEdit NAMI "/Volumes/ufs/.15177-179803682-1.txt"
15177 TextEdit NAMI "/Volumes/ufs/file.txt"
15177 TextEdit NAMI "._.15177-179803682-1.txt"
15177 TextEdit NAMI "._file.txt"
15177 TextEdit RET rename 0
<unlink, tidy up etc.>
- - - - - - - -
So as you can see, TextEdit or the layers of File Manager code
beneath it know how to save a file to a file system that supports
exchangedata(2), as well as how to back up a file and use rename to
atomically save a file on a file system that doesn't. The difference
in the two approaches is that in the latter case there's a window of
time where a lookup on the file will return ENOENT.
Only your ktrace from the file system you're working on will tell you
what's going wrong, but by comparing it to the HFS+ and UFS traces
you should be able to pinpoint the operation that causes the save
operation to your file system to be aborted.
Sam
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Darwin-kernel mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden