amosavian/AMSMB2

append data to already existing file?

Closed this issue · 9 comments

Thank you for providing this library!

I've been looking through the implementation and didn't find a way to write to an already existing file. Using

write<DataType>(data: DataType, toPath path: String, progress: WriteProgressHandler, completionHandler: SimpleCompletionHandler)

throws

Optional(Error Domain=NSPOSIXErrorDomain Code=17 "File exists" UserInfo={NSLocalizedFailureReason=Error code 17: Open failed with (0xc0000035) STATUS_OBJECT_NAME_COLLISION.})

because it's trying to create a new file.

I didn't expose any method to append data to existing files. Technically it should be poosible.

Thanks for your reply. I see.
file operations would probably a lot easier if there's a override and append option on the write method or some similar implementation.
Otherwise, I guess the only way is checking if the file exists -> then download -> edit -> delete the original file -> upload new file ?

i rechecked and found why I did not implement appending feature. libsmb2 does not implement lseek() with SEEK_END parameter in order to get to end of file. There are some workarounds. I'll try them.

Did you find a way around the libsmb2 limitation?

edit:
It seems like SEEK_END is in there but with limitations.
https://github.com/sahlberg/libsmb2/blob/6db01380d882a0ca1e4100f285c18c9e06df5585/include/smb2/libsmb2.h#L739

I implemented a workaround for myself, getting the current filesize via attributesOfItem(atPath:) and using that as an offset for pwrite(data:,offset:).
If you have the time, maybe you can let me know if you think that's OK as a workaround.

I implemented a workaround for myself, getting the current filesize via attributesOfItem(atPath:) and using that as an offset for pwrite(data:,offset:).
If you have the time, maybe you can let me know if you think that's OK as a workaround.

@MartinP7r Would you mind sharing your solution? I've tried the same thing (attributesOfItem(atPath:) combined with calling pwrite() instead of write()). It will make the initial write to create the file, but subsequent writes (at a non-zero offset) don't fail, but also don't seem to actually do anything. My test includes using O_RDWR | O_CREAT | O_APPEND when creating the SMB2FileHandle.

Any thoughts on what I'm doing wrong?

Update: so it looks like it'll write at any offset I provide, but it will never increase the file size. I don't know if that would help diagnose this problem...?

Hi @senojsitruc, sorry for the delayed answer.

Essentially I'm opening the file handle with O_RDWR | O_APPEND (I have other checks before that, so no O_CREAT, but yours should probably work) and then try handle.pwrite(data: data, offset: fileSize) where fileSize is the current size of the file (given by attributesOfItem(atPath:)).
This is called as a one-off, so no continued use of the same file handle.
It's working for my use-case and I had no complaints so far (couple of weeks).

I don't exactly understand what you mean by it'll write at any offset I provide, but it will never increase the file size.
Are you passing the filesize as offset to append at the end of the file?
You can look at

fileprivate func write(context: SMB2Context, from stream: InputStream, toPath: String,
for inspiration on how the offset works.

I don't exactly understand what you mean by it'll write at any offset I provide, but it will never increase the file size.
Are you passing the filesize as offset to append at the end of the file?

Thanks for your reply!

In this particular case, the file size from the initial creation of the file is 122 bytes. Whatever offset I provide to pwrite() is the offset at which it writes, except that it will not write beyond the 122 byte mark. In other words, I can write 122 bytes if I start at offset 0, or I can write 1 byte if I start at offset 121. Anything beyond 122 is magically dropped.

I've tried using pwrite() as well as lseek() (to move from .current to the end of the file, followed by a regular write()) -- the behavior is the same (as described above).

@MartinP7r

@MartinP7r It's now possible by append(data:toPath:offset:progress:) method to add new data to end of file. Please not you must have the file size if you want to have no data loss.