Skip to content

Implement ._hidden on Windows #2332

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

Merged
merged 1 commit into from
Jun 19, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 24 additions & 2 deletions Foundation/FileManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ fileprivate let UF_HIDDEN: Int32 = 1
#endif

import CoreFoundation
#if os(Windows)
import MSVCRT
#endif

open class FileManager : NSObject {

Expand Down Expand Up @@ -400,7 +403,6 @@ open class FileManager : NSObject {
newAccessDate = providedDate
}
#endif

case .immutable: fallthrough
case ._userImmutable:
prepareToSetOrUnsetFlag(UF_IMMUTABLE)
Expand All @@ -412,7 +414,21 @@ open class FileManager : NSObject {
prepareToSetOrUnsetFlag(UF_APPEND)

case ._hidden:
#if os(Windows)
let attrs = try windowsFileAttributes(atPath: path).dwFileAttributes
guard let isHidden = attributeValues[attribute] as? Bool else {
fatalError("Can't set \(attribute) to \(attributeValues[attribute] as Any?)")
}

let hiddenAttrs = isHidden
? attrs | DWORD(FILE_ATTRIBUTE_HIDDEN)
: attrs & DWORD(bitPattern: ~FILE_ATTRIBUTE_HIDDEN)
guard path.withCString(encodedAs: UTF16.self, { SetFileAttributesW($0, hiddenAttrs) }) else {
fatalError("Couldn't set \(path) to be hidden")
}
#else
prepareToSetOrUnsetFlag(UF_HIDDEN)
#endif

// FIXME: On Darwin, these can be set with setattrlist(); and of course chown/chgrp on other OSes.
case .ownerAccountID: fallthrough
Expand Down Expand Up @@ -531,7 +547,8 @@ open class FileManager : NSObject {

#if os(Windows)
result[.deviceIdentifier] = NSNumber(value: UInt64(s.st_rdev))
let type = FileAttributeType(attributes: try windowsFileAttributes(atPath: path), atPath: path)
let attributes = try windowsFileAttributes(atPath: path)
let type = FileAttributeType(attributes: attributes, atPath: path)
#else
if let pwd = getpwuid(s.st_uid), pwd.pointee.pw_name != nil {
let name = String(cString: pwd.pointee.pw_name)
Expand Down Expand Up @@ -566,6 +583,11 @@ open class FileManager : NSObject {
result[.appendOnly] = NSNumber(value: true)
}
#endif

#if os(Windows)
let attrs = attributes.dwFileAttributes
result[._hidden] = attrs & DWORD(FILE_ATTRIBUTE_HIDDEN) != 0
#endif
result[.ownerAccountID] = NSNumber(value: UInt64(s.st_uid))
result[.groupOwnerAccountID] = NSNumber(value: UInt64(s.st_gid))

Expand Down
36 changes: 32 additions & 4 deletions TestFoundation/TestFileManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -496,15 +496,18 @@ class TestFileManager : XCTestCase {
func test_directoryEnumerator() {
let fm = FileManager.default
let basePath = NSTemporaryDirectory() + "testdir\(NSUUID().uuidString)/"
let subDirs1 = basePath + "subdir1/subdir2/.hiddenDir/subdir3/"
let hiddenDir1 = basePath + "subdir1/subdir2/.hiddenDir/"
let subDirs1 = hiddenDir1 + "subdir3/"
let itemPath1 = basePath + "itemFile1"
#if os(Windows)
// Filenames ending with '.' are not valid on Windows, so don't bother testing them
let subDirs2 = basePath + "subdir1/subdir2/subdir4.app/subdir5/.subdir6.ext/subdir7.ext/"
let hiddenDir2 = basePath + "subdir1/subdir2/subdir4.app/subdir5/.subdir6.ext/"
let subDirs2 = hiddenDir2 + "subdir7.ext/"
let itemPath2 = subDirs1 + "itemFile2"
let itemPath3 = subDirs1 + "itemFile3.ext"
#else
let subDirs2 = basePath + "subdir1/subdir2/subdir4.app/subdir5./.subdir6.ext/subdir7.ext./"
let hiddenDir2 = basePath + "subdir1/subdir2/subdir4.app/subdir5./.subdir6.ext/"
let subDirs2 = hiddenDir2 + "subdir7.ext./"
let itemPath2 = subDirs1 + "itemFile2."
let itemPath3 = subDirs1 + "itemFile3.ext."
#endif
Expand Down Expand Up @@ -555,10 +558,35 @@ class TestFileManager : XCTestCase {

XCTAssertNotNil(try? fm.createDirectory(atPath: subDirs1, withIntermediateDirectories: true, attributes: nil))
XCTAssertNotNil(try? fm.createDirectory(atPath: subDirs2, withIntermediateDirectories: true, attributes: nil))
for filename in [itemPath1, itemPath2, itemPath3, hiddenItem1, hiddenItem2, hiddenItem3, hiddenItem4] {
for filename in [itemPath1, itemPath2, itemPath3] {
XCTAssertTrue(fm.createFile(atPath: filename, contents: Data(), attributes: nil), "Cant create file '\(filename)'")
}

var resourceValues = URLResourceValues()
resourceValues.isHidden = true
for filename in [ hiddenItem1, hiddenItem2, hiddenItem3, hiddenItem4] {
XCTAssertTrue(fm.createFile(atPath: filename, contents: Data(), attributes: nil), "Cant create file '\(filename)'")
#if os(Windows)
do {
var url = URL(fileURLWithPath: filename)
try url.setResourceValues(resourceValues)
} catch {
XCTFail("Couldn't make \(filename) a hidden file")
}
#endif
}

#if os(Windows)
do {
var hiddenURL1 = URL(fileURLWithPath: hiddenDir1)
var hiddenURL2 = URL(fileURLWithPath: hiddenDir2)
try hiddenURL1.setResourceValues(resourceValues)
try hiddenURL2.setResourceValues(resourceValues)
} catch {
XCTFail("Couldn't make \(hiddenDir1) and \(hiddenDir2) hidden directories")
}
#endif

if let foundItems = directoryItems(options: []) {
XCTAssertEqual(foundItems.count, fileLevels.count)
for (name, level) in foundItems {
Expand Down