-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Implement NSFileManager.copyItemAtPath #248
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -417,7 +417,38 @@ public class NSFileManager : NSObject { | |
} | ||
|
||
public func copyItemAtPath(srcPath: String, toPath dstPath: String) throws { | ||
NSUnimplemented() | ||
let fd_from = open(srcPath, O_RDONLY) | ||
if fd_from < 0 { | ||
throw _NSErrorWithErrno(errno, reading: true, path: dstPath, url: nil, extraUserInfo: nil) | ||
} | ||
defer { precondition(close(fd_from) >= 0) } | ||
let permission = (try! attributesOfItemAtPath(srcPath)[NSFilePosixPermissions] as! NSNumber).unsignedShortValue | ||
let fd_to = open(dstPath, O_WRONLY | O_CREAT | O_EXCL, permission) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what about directories? |
||
if fd_to < 0 { | ||
throw _NSErrorWithErrno(errno, reading: false, path: dstPath, url: nil, extraUserInfo: nil) | ||
} | ||
defer { precondition(close(fd_to) >= 0) } | ||
|
||
var buf = [UInt8](count: 4096, repeatedValue: 0) | ||
|
||
while true { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. perhaps a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We could do an fstat on each file and compare their We already find out the bytes read and written from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was suggesting that the stat could be done before the loop to determine the file size ahead of time to know how much would need to be read. |
||
let nread = read(fd_from, &buf, buf.count) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Darwin and BSDs have the API There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. On linux, sendfile only works for sockets, not files. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess this changed recently; sendfile works for files in Linux kernels above 2.6.33. source In that case we should probably use sendfile/copyfile, as long as we're satisfied to take a dependency on that kernel or later. Can you confirm we can do that? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. well There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using
I gather that this doesn't come free with Glibc? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It should be; perhaps the import isn't in the Glibc overlay for linux for #include <sys/sendfile.h> it would be totally reasonable to make a platform abstraction for copyfile in CFPlatform.c since Darwin is different than linux and if ever there is a Windows port there is a different function there too. |
||
if nread < 0 { throw _NSErrorWithErrno(errno, reading: true, path: srcPath, url: nil, extraUserInfo: nil) } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what about EAGAIN? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't believe EAGAIN can be raised here. The Darwin manpage has
In this case the file was not marked for non-blocking I/O, and the descriptor does not leak anywhere that someone might set it. Linux manpage is similar. |
||
if nread == 0 { break } | ||
var writeSlice = buf[0..<nread] | ||
|
||
while true { | ||
var nwritten: Int! = nil | ||
writeSlice.withUnsafeBufferPointer({ (ptr) -> () in | ||
nwritten = write(fd_to, ptr.baseAddress, ptr.count) | ||
}) | ||
if nwritten < 0 { | ||
throw _NSErrorWithErrno(errno, reading: false, path: dstPath, url: nil, extraUserInfo: nil) | ||
} | ||
writeSlice = writeSlice[writeSlice.startIndex.advancedBy(nwritten)..<writeSlice.endIndex] | ||
if writeSlice.count == 0 { break } | ||
} | ||
} | ||
} | ||
|
||
public func moveItemAtPath(srcPath: String, toPath dstPath: String) throws { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shouldn't this be a throwing case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Defer can't throw.
I could rewrite without defer, but it will be more brittle.