Skip to content

Commit ae4f64e

Browse files
authored
Merge pull request #1670 from rauhul/isDeletableFile
2 parents 04ad02f + bd0690a commit ae4f64e

File tree

2 files changed

+123
-2
lines changed

2 files changed

+123
-2
lines changed

Foundation/FileManager.swift

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -744,9 +744,41 @@ open class FileManager : NSObject {
744744
access($0, X_OK) == 0
745745
})
746746
}
747-
747+
748+
/**
749+
- parameters:
750+
- path: The path to the file we are trying to determine is deletable.
751+
752+
- returns: `true` if the file is deletable, `false` otherwise.
753+
*/
748754
open func isDeletableFile(atPath path: String) -> Bool {
749-
NSUnimplemented()
755+
// Get the parent directory of supplied path
756+
let parent = path._nsObject.deletingLastPathComponent
757+
758+
return _fileSystemRepresentation(withPath: parent, andPath: path, { parentFsRep, fsRep in
759+
// Check the parent directory is writeable, else return false.
760+
guard access(parentFsRep, W_OK) == 0 else {
761+
return false
762+
}
763+
764+
// Stat the parent directory, if that fails, return false.
765+
guard let parentS = try? _lstatFile(atPath: path, withFileSystemRepresentation: parentFsRep) else {
766+
return false
767+
}
768+
769+
// Check if the parent is 'sticky' if it exists.
770+
if (parentS.st_mode & S_ISVTX) == S_ISVTX {
771+
guard let s = try? _lstatFile(atPath: path, withFileSystemRepresentation: fsRep) else {
772+
return false
773+
}
774+
775+
// If the current user owns the file, return true.
776+
return s.st_uid == getuid()
777+
}
778+
779+
// Return true as the best guess.
780+
return true
781+
})
750782
}
751783

752784
private func _compareFiles(withFileSystemRepresentation file1Rep: UnsafePointer<Int8>, andFileSystemRepresentation file2Rep: UnsafePointer<Int8>, size: Int64, bufSize: Int) -> Bool {

TestFoundation/TestFileManager.swift

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ class TestFileManager : XCTestCase {
1616
("test_moveFile", test_moveFile),
1717
("test_fileSystemRepresentation", test_fileSystemRepresentation),
1818
("test_fileExists", test_fileExists),
19+
("test_isReadableFile", test_isReadableFile),
20+
("test_isWritableFile", test_isWritableFile),
21+
("test_isExecutableFile", test_isExecutableFile),
22+
("test_isDeletableFile", test_isDeletableFile),
1923
("test_fileAttributes", test_fileAttributes),
2024
("test_fileSystemAttributes", test_fileSystemAttributes),
2125
("test_setFileAttributes", test_setFileAttributes),
@@ -202,6 +206,91 @@ class TestFileManager : XCTestCase {
202206
ignoreError { try fm.removeItem(atPath: tmpDir.path) }
203207
}
204208

209+
func test_isReadableFile() {
210+
let fm = FileManager.default
211+
let path = NSTemporaryDirectory() + "test_isReadableFile\(NSUUID().uuidString)"
212+
213+
do {
214+
// create test file
215+
XCTAssertTrue(fm.createFile(atPath: path, contents: Data()))
216+
217+
// test unReadable if file has no permissions
218+
try fm.setAttributes([.posixPermissions : NSNumber(value: Int16(0o0000))], ofItemAtPath: path)
219+
XCTAssertFalse(fm.isReadableFile(atPath: path))
220+
221+
// test readable if file has read permissions
222+
try fm.setAttributes([.posixPermissions : NSNumber(value: Int16(0o0400))], ofItemAtPath: path)
223+
XCTAssertTrue(fm.isReadableFile(atPath: path))
224+
} catch let e {
225+
XCTFail("\(e)")
226+
}
227+
}
228+
229+
func test_isWritableFile() {
230+
let fm = FileManager.default
231+
let path = NSTemporaryDirectory() + "test_isWritableFile\(NSUUID().uuidString)"
232+
233+
do {
234+
// create test file
235+
XCTAssertTrue(fm.createFile(atPath: path, contents: Data()))
236+
237+
// test unWritable if file has no permissions
238+
try fm.setAttributes([.posixPermissions : NSNumber(value: Int16(0o0000))], ofItemAtPath: path)
239+
XCTAssertFalse(fm.isWritableFile(atPath: path))
240+
241+
// test writable if file has write permissions
242+
try fm.setAttributes([.posixPermissions : NSNumber(value: Int16(0o0200))], ofItemAtPath: path)
243+
XCTAssertTrue(fm.isWritableFile(atPath: path))
244+
} catch let e {
245+
XCTFail("\(e)")
246+
}
247+
}
248+
249+
func test_isExecutableFile() {
250+
let fm = FileManager.default
251+
let path = NSTemporaryDirectory() + "test_isExecutableFile\(NSUUID().uuidString)"
252+
253+
do {
254+
// create test file
255+
XCTAssertTrue(fm.createFile(atPath: path, contents: Data()))
256+
257+
// test unExecutable if file has no permissions
258+
try fm.setAttributes([.posixPermissions : NSNumber(value: Int16(0o0000))], ofItemAtPath: path)
259+
XCTAssertFalse(fm.isExecutableFile(atPath: path))
260+
261+
// test executable if file has execute permissions
262+
try fm.setAttributes([.posixPermissions : NSNumber(value: Int16(0o0100))], ofItemAtPath: path)
263+
XCTAssertTrue(fm.isExecutableFile(atPath: path))
264+
} catch let e {
265+
XCTFail("\(e)")
266+
}
267+
}
268+
269+
func test_isDeletableFile() {
270+
let fm = FileManager.default
271+
272+
do {
273+
let dir_path = NSTemporaryDirectory() + "/test_isDeletableFile_dir/"
274+
let file_path = dir_path + "test_isDeletableFile\(NSUUID().uuidString)"
275+
// create test directory
276+
try fm.createDirectory(atPath: dir_path, withIntermediateDirectories: true)
277+
// create test file
278+
XCTAssertTrue(fm.createFile(atPath: file_path, contents: Data()))
279+
280+
// test undeletable if parent directory has no permissions
281+
try fm.setAttributes([.posixPermissions : NSNumber(value: Int16(0o0000))], ofItemAtPath: dir_path)
282+
XCTAssertFalse(fm.isDeletableFile(atPath: file_path))
283+
284+
// test deletable if parent directory has all necessary permissions
285+
try fm.setAttributes([.posixPermissions : NSNumber(value: Int16(0o0755))], ofItemAtPath: dir_path)
286+
XCTAssertTrue(fm.isDeletableFile(atPath: file_path))
287+
}
288+
catch { XCTFail("\(error)") }
289+
290+
// test against known undeletable file
291+
XCTAssertFalse(fm.isDeletableFile(atPath: "/dev/null"))
292+
}
293+
205294
func test_fileAttributes() {
206295
let fm = FileManager.default
207296
let path = NSTemporaryDirectory() + "test_fileAttributes\(NSUUID().uuidString)"

0 commit comments

Comments
 (0)