[GH-ISSUE #375] Can't write after lseek() #194

Closed
opened 2026-03-04 01:43:09 +03:00 by kerem · 5 comments
Owner

Originally created by @RobbKistler on GitHub (Mar 16, 2016).
Original GitHub issue: https://github.com/s3fs-fuse/s3fs-fuse/issues/375

Version:
s3fs-fuse master at cf56b35766

Repro:
As part of a bigger test (cp -R /usr/lib abucket/), I found that copying a certain file into an s3fs-fuse mount point fails:

cp  /usr/lib/locale/locale-archive abucket/bug/

The cp always fails on the file locale-archive because the source file is sparse. It has "holes" in it because it was written using lseek() to advance past the end of the file. The cp command is smart, and tries to do the same lseek() when writing the file into the s3fs-fuse mount point. Since this file might not be sparse on all systems, I will create a PR with a test that uses lseek() directly.

Results:
The file is partially written, and then cp gets this error:

cp: writing `abucket/bug/locale-archive': Operation not permitted

while s3fs-fuse reports this error:

s3fs: [DBG] s3fs.cpp:s3fs_write(2167): [path=/bug/locale-archive][size=4096][offset=61440][fd=4]
s3fs: [DBG] fdcache.cpp:ExistOpen(1896): [path=/bug/locale-archive][fd=4][ignore_existfd=false]
s3fs: [DBG] fdcache.cpp:Open(1845): [path=/bug/locale-archive][size=-1][time=-1]
s3fs: [DBG] fdcache.cpp:Dup(712): [path=/bug/locale-archive][fd=4][refcnt=2]
s3fs: [DBG] fdcache.cpp:Write(1503): [path=/bug/locale-archive][fd=4][offset=61440][size=4096]
s3fs: [DBG] fdcache.cpp:Load(1027): [path=/bug/locale-archive][fd=4][offset=0][size=61440]
s3fs: [INF]       curl.cpp:GetObjectRequest(2632): [tpath=/bug/locale-archive][start=12288][size=0]
s3fs: [DBG] s3fs.cpp:get_object_attribute(417): [path=/bug/locale-archive]
s3fs: [DBG] cache.cpp:GetStat(259): stat cache hit [path=/bug/locale-archive][time=176085.960913293][hit count=5]
s3fs: [INF]       curl.cpp:PreGetObjectRequest(2562): [tpath=/bug/locale-archive][start=12288][size=0]
s3fs: [ERR] fdcache.cpp:Write(1527): failed to load uninitialized area before writing(errno=-1)
s3fs: [WAN] s3fs.cpp:s3fs_write(2178): failed to write file(/bug/locale-archive). result=-1

The "failed to load uninitialized area before writing" happens because S3fsCurl::PreGetObjectRequest() returns an error because the value of the size parameter is 0.

This is what a strace of cp looks like. fd=3 is the source file, fd=4 is the target file on the s3fs-fuse mount. The FS_IOC_FIEMAP ioctl is a big hint that cp has detected a sparse source file.

open("/usr/lib/locale/locale-archive", O_RDONLY) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=1534672, ...}) = 0
open("rks3fs/bug/locale-archive", O_WRONLY|O_CREAT|O_EXCL, 0644) = 4
fstat(4, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
ioctl(3, FS_IOC_FIEMAP, 0x7fff58639b60) = 0
read(3, "\t\1\2\336\0\0\0\0008\0\0\0\1\0\0\0\213\3\0\0\274*\0\0\v\0\0\0L\35\0\0"..., 12288) = 12288
write(4, "\t\1\2\336\0\0\0\0008\0\0\0\1\0\0\0\213\3\0\0\274*\0\0\v\0\0\0L\35\0\0"..., 12288) = 12288
lseek(3, 16384, SEEK_SET)               = 16384
lseek(4, 16384, SEEK_SET)               = 16384
read(3, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 4096) = 4096
write(4, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 4096) = 4096
lseek(3, 61440, SEEK_SET)               = 61440
lseek(4, 61440, SEEK_SET)               = 61440
read(3, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 4096) = 4096
write(4, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 4096) = -1 EPERM (Operation not permitted)

Expected:
s3fs-fuse should write alll 0's for the bytes in the "hole" created by the lseek().

Originally created by @RobbKistler on GitHub (Mar 16, 2016). Original GitHub issue: https://github.com/s3fs-fuse/s3fs-fuse/issues/375 **Version**: s3fs-fuse master at cf56b35766c4450c789968335ffbc01258cbbdb8 **Repro:** As part of a bigger test (`cp -R /usr/lib abucket/`), I found that copying a certain file into an s3fs-fuse mount point fails: ``` cp /usr/lib/locale/locale-archive abucket/bug/ ``` The cp always fails on the file locale-archive because the source file is [sparse](https://en.wikipedia.org/wiki/Sparse_file#Sparse_files_in_Unix). It has "holes" in it because it was written using lseek() to advance past the end of the file. The cp command is smart, and tries to do the same lseek() when writing the file into the s3fs-fuse mount point. Since this file might not be sparse on all systems, I will create a PR with a test that uses lseek() directly. **Results:** The file is partially written, and then `cp` gets this error: ``` cp: writing `abucket/bug/locale-archive': Operation not permitted ``` while s3fs-fuse reports this error: ``` s3fs: [DBG] s3fs.cpp:s3fs_write(2167): [path=/bug/locale-archive][size=4096][offset=61440][fd=4] s3fs: [DBG] fdcache.cpp:ExistOpen(1896): [path=/bug/locale-archive][fd=4][ignore_existfd=false] s3fs: [DBG] fdcache.cpp:Open(1845): [path=/bug/locale-archive][size=-1][time=-1] s3fs: [DBG] fdcache.cpp:Dup(712): [path=/bug/locale-archive][fd=4][refcnt=2] s3fs: [DBG] fdcache.cpp:Write(1503): [path=/bug/locale-archive][fd=4][offset=61440][size=4096] s3fs: [DBG] fdcache.cpp:Load(1027): [path=/bug/locale-archive][fd=4][offset=0][size=61440] s3fs: [INF] curl.cpp:GetObjectRequest(2632): [tpath=/bug/locale-archive][start=12288][size=0] s3fs: [DBG] s3fs.cpp:get_object_attribute(417): [path=/bug/locale-archive] s3fs: [DBG] cache.cpp:GetStat(259): stat cache hit [path=/bug/locale-archive][time=176085.960913293][hit count=5] s3fs: [INF] curl.cpp:PreGetObjectRequest(2562): [tpath=/bug/locale-archive][start=12288][size=0] s3fs: [ERR] fdcache.cpp:Write(1527): failed to load uninitialized area before writing(errno=-1) s3fs: [WAN] s3fs.cpp:s3fs_write(2178): failed to write file(/bug/locale-archive). result=-1 ``` The "failed to load uninitialized area before writing" happens because S3fsCurl::PreGetObjectRequest() returns an error because the value of the size parameter is 0. This is what a strace of cp looks like. fd=3 is the source file, fd=4 is the target file on the s3fs-fuse mount. The FS_IOC_FIEMAP ioctl is a big hint that cp has detected a sparse source file. ``` open("/usr/lib/locale/locale-archive", O_RDONLY) = 3 fstat(3, {st_mode=S_IFREG|0644, st_size=1534672, ...}) = 0 open("rks3fs/bug/locale-archive", O_WRONLY|O_CREAT|O_EXCL, 0644) = 4 fstat(4, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0 ioctl(3, FS_IOC_FIEMAP, 0x7fff58639b60) = 0 read(3, "\t\1\2\336\0\0\0\0008\0\0\0\1\0\0\0\213\3\0\0\274*\0\0\v\0\0\0L\35\0\0"..., 12288) = 12288 write(4, "\t\1\2\336\0\0\0\0008\0\0\0\1\0\0\0\213\3\0\0\274*\0\0\v\0\0\0L\35\0\0"..., 12288) = 12288 lseek(3, 16384, SEEK_SET) = 16384 lseek(4, 16384, SEEK_SET) = 16384 read(3, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 4096) = 4096 write(4, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 4096) = 4096 lseek(3, 61440, SEEK_SET) = 61440 lseek(4, 61440, SEEK_SET) = 61440 read(3, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 4096) = 4096 write(4, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 4096) = -1 EPERM (Operation not permitted) ``` **Expected:** s3fs-fuse should write alll 0's for the bytes in the "hole" created by the lseek().
kerem closed this issue 2026-03-04 01:43:09 +03:00
Author
Owner

@ggtakec commented on GitHub (Mar 22, 2016):

@RobbKistler Fixed this issue by #379.

s3fs did not take into account the case of sparsed file.
When FUSE copies the sparsed file, FUSE seems to call truncate internally or open with file size for seeking.

s3fs makes the cache file(or temporary file) which is sparsed file(by using truncate system call), then s3fs should seek the file when writing to that cache file before uploading it to S3.
And we have to change the file size, which size is used to loading contents before uploading if there is area which has not been downloading yet.

I tested following:

$ rm /mnt/s3/locale-archive
$ cp /usr/lib/locale/locale-archive /mnt/s3/
$ diff /usr/lib/locale/locale-archive /mnt/s3/locale-archive

# for over write
$ cp /usr/lib/locale/locale-archive /mnt/s3/
$ diff /usr/lib/locale/locale-archive /mnt/s3/locale-archive

# for sparsed file
$ echo test_data_1 > /tmp/testfile
$ truncate --size=8192 /tmp/testfile
$ echo test_data_2 >> /tmp/testfile
$ truncate --size=16384 /tmp/testfile
$ cp /tmp/testfile /mnt/s3/
$ diff /tmp/testfile /mnt/s3/testfile

Regards,

<!-- gh-comment-id:199651073 --> @ggtakec commented on GitHub (Mar 22, 2016): @RobbKistler Fixed this issue by #379. s3fs did not take into account the case of sparsed file. When FUSE copies the sparsed file, FUSE seems to call truncate internally or open with file size for seeking. s3fs makes the cache file(or temporary file) which is sparsed file(by using truncate system call), then s3fs should seek the file when writing to that cache file before uploading it to S3. And we have to change the file size, which size is used to loading contents before uploading if there is area which has not been downloading yet. I tested following: ``` $ rm /mnt/s3/locale-archive $ cp /usr/lib/locale/locale-archive /mnt/s3/ $ diff /usr/lib/locale/locale-archive /mnt/s3/locale-archive # for over write $ cp /usr/lib/locale/locale-archive /mnt/s3/ $ diff /usr/lib/locale/locale-archive /mnt/s3/locale-archive # for sparsed file $ echo test_data_1 > /tmp/testfile $ truncate --size=8192 /tmp/testfile $ echo test_data_2 >> /tmp/testfile $ truncate --size=16384 /tmp/testfile $ cp /tmp/testfile /mnt/s3/ $ diff /tmp/testfile /mnt/s3/testfile ``` Regards,
Author
Owner

@RobbKistler commented on GitHub (Mar 23, 2016):

Thank you so much @ggtakec!

<!-- gh-comment-id:200529385 --> @RobbKistler commented on GitHub (Mar 23, 2016): Thank you so much @ggtakec!
Author
Owner

@ggtakec commented on GitHub (Apr 10, 2016):

Merged #376

<!-- gh-comment-id:207913380 --> @ggtakec commented on GitHub (Apr 10, 2016): Merged #376
Author
Owner

@ggtakec commented on GitHub (Apr 12, 2016):

@RobbKistler #379 fixed this issue, but it made a new bug(Not made a decent cache files).
So I revert #379, and will make patches for this.
Thus I reopen this issue.

<!-- gh-comment-id:208999499 --> @ggtakec commented on GitHub (Apr 12, 2016): @RobbKistler #379 fixed this issue, but it made a new bug(Not made a decent cache files). So I revert #379, and will make patches for this. Thus I reopen this issue.
Author
Owner

@ggtakec commented on GitHub (Apr 12, 2016):

@RobbKistler I merged #395 which fixed cache broken.
I re-closed this issue, if you find a bug please reopen this.
thanks in advance for your help.

<!-- gh-comment-id:209047630 --> @ggtakec commented on GitHub (Apr 12, 2016): @RobbKistler I merged #395 which fixed cache broken. I re-closed this issue, if you find a bug please reopen this. thanks in advance for your help.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/s3fs-fuse#194
No description provided.