win32-file-attributes 1.0.3 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/CHANGES +21 -16
- data/MANIFEST +12 -10
- data/README +46 -46
- data/Rakefile +29 -32
- data/certs/djberg96_pub.pem +21 -0
- data/lib/win32-file-attributes.rb +1 -0
- data/lib/win32/file/attributes.rb +569 -569
- data/lib/win32/file/windows/functions.rb +50 -50
- data/test/test_win32_file_attribute_constants.rb +25 -25
- data/test/test_win32_file_attributes.rb +386 -386
- data/win32-file-attributes.gemspec +24 -23
- metadata +28 -4
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 497d0296b633a20cb8ee04f2f2367646fa7c3262
|
4
|
+
data.tar.gz: ca978fbcd970c2bf542b423efe3bfa602493a0fb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b9f5b84a26f35e71103520b4e052cfadfacc70a5669273137b6fc5cbd3615ef6354343036f2b73d5a57b47f578816c0c5432b97f9a90a257417febca8d446906
|
7
|
+
data.tar.gz: ed6c2f906487368007d40a8f1d7465e9a3500591200c8e6b681f67aedc5f74e03cf02a2e0d550f081abe1d447ed497babb8563ccf0b2c91fdce7f590f373ef8a
|
checksums.yaml.gz.sig
ADDED
Binary file
|
data.tar.gz.sig
ADDED
Binary file
|
data/CHANGES
CHANGED
@@ -1,16 +1,21 @@
|
|
1
|
-
= 1.0.
|
2
|
-
*
|
3
|
-
*
|
4
|
-
*
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
*
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
*
|
14
|
-
|
15
|
-
= 1.0.
|
16
|
-
*
|
1
|
+
= 1.0.4 - 27-Oct-2015
|
2
|
+
* This gem is now signed.
|
3
|
+
* Added a win32-file-attributes.rb file for convenience.
|
4
|
+
* All gem related tasks in the Rakefile now assume Rubygems 2.x.
|
5
|
+
|
6
|
+
= 1.0.3 - 8-Nov-2014
|
7
|
+
* Make FFI functions private.
|
8
|
+
* Removed reference to Rubyforge in gemspec.
|
9
|
+
* Use require_relative internally.
|
10
|
+
* Explicitly free some FFI memory pointers where appropriate.
|
11
|
+
|
12
|
+
= 1.0.2 - 28-Apr-2014
|
13
|
+
* The singleton methods now accept arguments that implement to_str or to_path.
|
14
|
+
|
15
|
+
= 1.0.1 - 8-Apr-2013
|
16
|
+
* Fixed the HANDLE prototypes in the underlying FFI code for 64 bit Ruby.
|
17
|
+
* Fixed the return type for SetFileAttributes, and adjusted the code to match.
|
18
|
+
* Added some tests for constants.
|
19
|
+
|
20
|
+
= 1.0.0 - 26-Nov-2012
|
21
|
+
* Initial release as an independent library.
|
data/MANIFEST
CHANGED
@@ -1,10 +1,12 @@
|
|
1
|
-
* CHANGES
|
2
|
-
* MANIFEST
|
3
|
-
* Rakefile
|
4
|
-
* README
|
5
|
-
* win32-file-attributes.gemspec
|
6
|
-
*
|
7
|
-
* lib/win32
|
8
|
-
* lib/win32/file/
|
9
|
-
* lib/win32/file/windows/
|
10
|
-
*
|
1
|
+
* CHANGES
|
2
|
+
* MANIFEST
|
3
|
+
* Rakefile
|
4
|
+
* README
|
5
|
+
* win32-file-attributes.gemspec
|
6
|
+
* certs/djberg96_pub.pem
|
7
|
+
* lib/win32-file-attributes.rb
|
8
|
+
* lib/win32/file/attributes.rb
|
9
|
+
* lib/win32/file/windows/constants.rb
|
10
|
+
* lib/win32/file/windows/functions.rb
|
11
|
+
* lib/win32/file/windows/structs.rb
|
12
|
+
* test/test_win32_file_attributes.rb
|
data/README
CHANGED
@@ -1,46 +1,46 @@
|
|
1
|
-
== Description
|
2
|
-
Additional methods for the File class that relate to file properties
|
3
|
-
on the Microsoft Windows operating system.
|
4
|
-
|
5
|
-
== Prerequisites
|
6
|
-
* ffi
|
7
|
-
|
8
|
-
== Installation
|
9
|
-
gem install win32-file-attributes
|
10
|
-
|
11
|
-
== Synopsis
|
12
|
-
|
13
|
-
require 'win32/file/attributes
|
14
|
-
|
15
|
-
p File.hidden?(file) # => false
|
16
|
-
p File.attributes(file) # => ['archive', 'indexed']
|
17
|
-
|
18
|
-
== Notes
|
19
|
-
If you have the win32-file gem already installed then you do not need this
|
20
|
-
gem. This library was split out from the win32-file gem in order to ease
|
21
|
-
testing and maintenance.
|
22
|
-
|
23
|
-
Otherwise, the only difference is that this library uses FFI instead
|
24
|
-
of win32-api. This also means that it works with JRuby.
|
25
|
-
|
26
|
-
== Known issues or bugs
|
27
|
-
None that I'm aware of.
|
28
|
-
|
29
|
-
Please report any issues you find on the github page at:
|
30
|
-
|
31
|
-
https://github.com/djberg96/win32-file-attributes/issues
|
32
|
-
|
33
|
-
== License
|
34
|
-
Artistic 2.0
|
35
|
-
|
36
|
-
== Copyright
|
37
|
-
(C) 2003-2014, Daniel J. Berger, All Rights Reserved
|
38
|
-
|
39
|
-
== Warranty
|
40
|
-
This package is provided "as is" and without any express or
|
41
|
-
implied warranties, including, without limitation, the implied
|
42
|
-
warranties of merchantability and fitness for a particular purpose.
|
43
|
-
|
44
|
-
== Authors
|
45
|
-
* Daniel J. Berger
|
46
|
-
* Park Heesob
|
1
|
+
== Description
|
2
|
+
Additional methods for the File class that relate to file properties
|
3
|
+
on the Microsoft Windows operating system.
|
4
|
+
|
5
|
+
== Prerequisites
|
6
|
+
* ffi
|
7
|
+
|
8
|
+
== Installation
|
9
|
+
gem install win32-file-attributes
|
10
|
+
|
11
|
+
== Synopsis
|
12
|
+
|
13
|
+
require 'win32/file/attributes
|
14
|
+
|
15
|
+
p File.hidden?(file) # => false
|
16
|
+
p File.attributes(file) # => ['archive', 'indexed']
|
17
|
+
|
18
|
+
== Notes
|
19
|
+
If you have the win32-file gem already installed then you do not need this
|
20
|
+
gem. This library was split out from the win32-file gem in order to ease
|
21
|
+
testing and maintenance.
|
22
|
+
|
23
|
+
Otherwise, the only difference is that this library uses FFI instead
|
24
|
+
of win32-api. This also means that it works with JRuby.
|
25
|
+
|
26
|
+
== Known issues or bugs
|
27
|
+
None that I'm aware of.
|
28
|
+
|
29
|
+
Please report any issues you find on the github page at:
|
30
|
+
|
31
|
+
https://github.com/djberg96/win32-file-attributes/issues
|
32
|
+
|
33
|
+
== License
|
34
|
+
Artistic 2.0
|
35
|
+
|
36
|
+
== Copyright
|
37
|
+
(C) 2003-2014, Daniel J. Berger, All Rights Reserved
|
38
|
+
|
39
|
+
== Warranty
|
40
|
+
This package is provided "as is" and without any express or
|
41
|
+
implied warranties, including, without limitation, the implied
|
42
|
+
warranties of merchantability and fitness for a particular purpose.
|
43
|
+
|
44
|
+
== Authors
|
45
|
+
* Daniel J. Berger
|
46
|
+
* Park Heesob
|
data/Rakefile
CHANGED
@@ -1,32 +1,29 @@
|
|
1
|
-
require 'rake'
|
2
|
-
require 'rake/clean'
|
3
|
-
require 'rake/testtask'
|
4
|
-
|
5
|
-
CLEAN.include('**/*.gem', '**/*.rbc')
|
6
|
-
|
7
|
-
namespace :gem do
|
8
|
-
desc 'Build the win32-file-attributes gem'
|
9
|
-
task :create => [:clean] do
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
end
|
31
|
-
|
32
|
-
task :default => :test
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/clean'
|
3
|
+
require 'rake/testtask'
|
4
|
+
|
5
|
+
CLEAN.include('**/*.gem', '**/*.rbc')
|
6
|
+
|
7
|
+
namespace :gem do
|
8
|
+
desc 'Build the win32-file-attributes gem'
|
9
|
+
task :create => [:clean] do
|
10
|
+
require 'rubygems/package'
|
11
|
+
spec = eval(IO.read('win32-file-attributes.gemspec'))
|
12
|
+
spec.signing_key = File.join(Dir.home, '.ssh', 'gem-private_key.pem')
|
13
|
+
Gem::Package.build(spec)
|
14
|
+
end
|
15
|
+
|
16
|
+
desc "Install the win32-file-attributes gem"
|
17
|
+
task :install => [:create] do
|
18
|
+
file = Dir["*.gem"].first
|
19
|
+
sh "gem install -l #{file}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
Rake::TestTask.new do |t|
|
24
|
+
task :test => :clean
|
25
|
+
t.warning = true
|
26
|
+
t.verbose = true
|
27
|
+
end
|
28
|
+
|
29
|
+
task :default => :test
|
@@ -0,0 +1,21 @@
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
2
|
+
MIIDcDCCAligAwIBAgIBATANBgkqhkiG9w0BAQUFADA/MREwDwYDVQQDDAhkamJl
|
3
|
+
cmc5NjEVMBMGCgmSJomT8ixkARkWBWdtYWlsMRMwEQYKCZImiZPyLGQBGRYDY29t
|
4
|
+
MB4XDTE1MDkwMjIwNDkxOFoXDTE2MDkwMTIwNDkxOFowPzERMA8GA1UEAwwIZGpi
|
5
|
+
ZXJnOTYxFTATBgoJkiaJk/IsZAEZFgVnbWFpbDETMBEGCgmSJomT8ixkARkWA2Nv
|
6
|
+
bTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMyTkvXqRp6hLs9eoJOS
|
7
|
+
Hmi8kRYbq9Vkf15/hMxJpotYMgJVHHWrmDcC5Dye2PbnXjTkKf266Zw0PtT9h+lI
|
8
|
+
S3ts9HO+vaCFSMwFFZmnWJSpQ3CNw2RcHxjWkk9yF7imEM8Kz9ojhiDXzBetdV6M
|
9
|
+
gr0lV/alUr7TNVBDngbXEfTWscyXh1qd7xZ4EcOdsDktCe5G45N/o3662tPQvJsi
|
10
|
+
FOF0CM/KuBsa/HL1/eoEmF4B3EKIRfTHrQ3hu20Kv3RJ88QM4ec2+0dd97uX693O
|
11
|
+
zv6981fyEg+aXLkxrkViM/tz2qR2ZE0jPhHTREPYeMEgptRkTmWSKAuLVWrJEfgl
|
12
|
+
DtkCAwEAAaN3MHUwCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwHQYDVR0OBBYEFEwe
|
13
|
+
nn6bfJADmuIDiMSOzedOrL+xMB0GA1UdEQQWMBSBEmRqYmVyZzk2QGdtYWlsLmNv
|
14
|
+
bTAdBgNVHRIEFjAUgRJkamJlcmc5NkBnbWFpbC5jb20wDQYJKoZIhvcNAQEFBQAD
|
15
|
+
ggEBAHmNOCWoDVD75zHFueY0viwGDVP1BNGFC+yXcb7u2GlK+nEMCORqzURbYPf7
|
16
|
+
tL+/hzmePIRz7i30UM//64GI1NLv9jl7nIwjhPpXpf7/lu2I9hOTsvwSumb5UiKC
|
17
|
+
/sqBxI3sfj9pr79Wpv4MuikX1XPik7Ncb7NPsJPw06Lvyc3Hkg5X2XpPtLtS+Gr2
|
18
|
+
wKJnmzb5rIPS1cmsqv0M9LPWflzfwoZ/SpnmhagP+g05p8bRNKjZSA2iImM/GyYZ
|
19
|
+
EJYzxdPOrx2n6NYR3Hk+vHP0U7UBSveI6+qx+ndQYaeyCn+GRX2PKS9h66YF/Q1V
|
20
|
+
tGSHgAmcLlkdGgan182qsE/4kKM=
|
21
|
+
-----END CERTIFICATE-----
|
@@ -0,0 +1 @@
|
|
1
|
+
require_relative 'win32/file/attributes'
|
@@ -1,569 +1,569 @@
|
|
1
|
-
require_relative 'windows/constants'
|
2
|
-
require_relative 'windows/structs'
|
3
|
-
require_relative 'windows/functions'
|
4
|
-
|
5
|
-
class File
|
6
|
-
include Windows::File::Constants
|
7
|
-
include Windows::File::Functions
|
8
|
-
extend Windows::File::Constants
|
9
|
-
extend Windows::File::Structs
|
10
|
-
extend Windows::File::Functions
|
11
|
-
|
12
|
-
# The version of the win32-file library
|
13
|
-
WIN32_FILE_ATTRIBUTE_VERSION = '1.0.
|
14
|
-
|
15
|
-
## ABBREVIATED ATTRIBUTE CONSTANTS
|
16
|
-
|
17
|
-
# The file or directory is an archive. Typically used to mark files for
|
18
|
-
# backup or removal.
|
19
|
-
ARCHIVE = FILE_ATTRIBUTE_ARCHIVE
|
20
|
-
|
21
|
-
# The file or directory is encrypted. For a file, this means that all
|
22
|
-
# data in the file is encrypted. For a directory, this means that
|
23
|
-
# encryption is # the default for newly created files and subdirectories.
|
24
|
-
COMPRESSED = FILE_ATTRIBUTE_COMPRESSED
|
25
|
-
|
26
|
-
# The file is hidden. Not included in an ordinary directory listing.
|
27
|
-
HIDDEN = FILE_ATTRIBUTE_HIDDEN
|
28
|
-
|
29
|
-
# A file that does not have any other attributes set.
|
30
|
-
NORMAL = FILE_ATTRIBUTE_NORMAL
|
31
|
-
|
32
|
-
# The data of a file is not immediately available. This attribute indicates
|
33
|
-
# that file data is physically moved to offline storage.
|
34
|
-
OFFLINE = FILE_ATTRIBUTE_OFFLINE
|
35
|
-
|
36
|
-
# The file is read only. Apps can read it, but not write to it or delete it.
|
37
|
-
READONLY = FILE_ATTRIBUTE_READONLY
|
38
|
-
|
39
|
-
# The file is part of or used exclusively by an operating system.
|
40
|
-
SYSTEM = FILE_ATTRIBUTE_SYSTEM
|
41
|
-
|
42
|
-
# The file is being used for temporary storage.
|
43
|
-
TEMPORARY = FILE_ATTRIBUTE_TEMPORARY
|
44
|
-
|
45
|
-
# The file or directory is to be indexed by the content indexing service.
|
46
|
-
# Note that we have inverted the traditional definition.
|
47
|
-
INDEXED = 0x0002000
|
48
|
-
|
49
|
-
# Synonym for File::INDEXED.
|
50
|
-
CONTENT_INDEXED = INDEXED
|
51
|
-
|
52
|
-
## SINGLETON METHODS
|
53
|
-
|
54
|
-
# Returns an array of strings indicating the attributes for that file.
|
55
|
-
# The possible values are:
|
56
|
-
#
|
57
|
-
# archive
|
58
|
-
# compressed
|
59
|
-
# directory
|
60
|
-
# encrypted
|
61
|
-
# hidden
|
62
|
-
# indexed
|
63
|
-
# normal
|
64
|
-
# offline
|
65
|
-
# readonly
|
66
|
-
# reparse_point
|
67
|
-
# sparse
|
68
|
-
# system
|
69
|
-
# temporary
|
70
|
-
#
|
71
|
-
def self.attributes(file)
|
72
|
-
attributes = GetFileAttributesW(string_check(file).wincode)
|
73
|
-
|
74
|
-
if attributes == INVALID_FILE_ATTRIBUTES
|
75
|
-
raise SystemCallError.new("GetFileAttributes", FFI.errno)
|
76
|
-
end
|
77
|
-
|
78
|
-
arr = []
|
79
|
-
|
80
|
-
arr << 'archive' if attributes & FILE_ATTRIBUTE_ARCHIVE > 0
|
81
|
-
arr << 'compressed' if attributes & FILE_ATTRIBUTE_COMPRESSED > 0
|
82
|
-
arr << 'directory' if attributes & FILE_ATTRIBUTE_DIRECTORY > 0
|
83
|
-
arr << 'encrypted' if attributes & FILE_ATTRIBUTE_ENCRYPTED > 0
|
84
|
-
arr << 'hidden' if attributes & FILE_ATTRIBUTE_HIDDEN > 0
|
85
|
-
arr << 'indexed' if attributes & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED == 0
|
86
|
-
arr << 'normal' if attributes & FILE_ATTRIBUTE_NORMAL > 0
|
87
|
-
arr << 'offline' if attributes & FILE_ATTRIBUTE_OFFLINE > 0
|
88
|
-
arr << 'readonly' if attributes & FILE_ATTRIBUTE_READONLY > 0
|
89
|
-
arr << 'reparse_point' if attributes & FILE_ATTRIBUTE_REPARSE_POINT > 0
|
90
|
-
arr << 'sparse' if attributes & FILE_ATTRIBUTE_SPARSE_FILE > 0
|
91
|
-
arr << 'system' if attributes & FILE_ATTRIBUTE_SYSTEM > 0
|
92
|
-
arr << 'temporary' if attributes & FILE_ATTRIBUTE_TEMPORARY > 0
|
93
|
-
|
94
|
-
arr
|
95
|
-
end
|
96
|
-
|
97
|
-
# Sets the file attributes based on the given (numeric) +flags+. This does
|
98
|
-
# not remove existing attributes, it merely adds to them. Use the
|
99
|
-
# File.remove_attributes method if you want to remove them.
|
100
|
-
#
|
101
|
-
# Please not that certain attributes cannot always be applied. For example,
|
102
|
-
# you cannot convert a regular file into a directory. Common sense should
|
103
|
-
# guide you here.
|
104
|
-
#
|
105
|
-
def self.set_attributes(file, flags)
|
106
|
-
wfile = string_check(file).wincode
|
107
|
-
attributes = GetFileAttributesW(wfile)
|
108
|
-
|
109
|
-
if attributes == INVALID_FILE_ATTRIBUTES
|
110
|
-
raise SystemCallError.new("GetFileAttributes", FFI.errno)
|
111
|
-
end
|
112
|
-
|
113
|
-
attributes |= flags
|
114
|
-
|
115
|
-
unless SetFileAttributesW(wfile, attributes)
|
116
|
-
raise SystemCallError.new("SetFileAttributes", FFI.errno)
|
117
|
-
end
|
118
|
-
|
119
|
-
self
|
120
|
-
end
|
121
|
-
|
122
|
-
# Removes the file attributes based on the given (numeric) +flags+.
|
123
|
-
#
|
124
|
-
def self.remove_attributes(file, flags)
|
125
|
-
wfile = string_check(file).wincode
|
126
|
-
attributes = GetFileAttributesW(wfile)
|
127
|
-
|
128
|
-
if attributes == INVALID_FILE_ATTRIBUTES
|
129
|
-
raise SystemCallError.new("GetFileAttributes", FFI.errno)
|
130
|
-
end
|
131
|
-
|
132
|
-
attributes &= ~flags
|
133
|
-
|
134
|
-
unless SetFileAttributesW(wfile, attributes)
|
135
|
-
raise SystemCallError.new("SetFileAttributes", FFI.errno)
|
136
|
-
end
|
137
|
-
|
138
|
-
self
|
139
|
-
end
|
140
|
-
|
141
|
-
# Returns whether or not the file or directory is an archive file or
|
142
|
-
# directory. Applications typically use this attribute to mark files
|
143
|
-
# for backup or removal.
|
144
|
-
#
|
145
|
-
def self.archive?(file)
|
146
|
-
check_for_attribute(file, FILE_ATTRIBUTE_ARCHIVE)
|
147
|
-
end
|
148
|
-
|
149
|
-
# Returns whether or not the file or directory is compressed. For a file,
|
150
|
-
# this means that all of the data in the file is compressed. For a directory,
|
151
|
-
# this means that compression is the default for newly created files and
|
152
|
-
# subdirectories.
|
153
|
-
#
|
154
|
-
def self.compressed?(file)
|
155
|
-
check_for_attribute(file, FILE_ATTRIBUTE_COMPRESSED)
|
156
|
-
end
|
157
|
-
|
158
|
-
# Returns whether or not the file or directory is encrypted. For a file,
|
159
|
-
# this means that all data in the file is encrypted. For a directory, this
|
160
|
-
# means that encryption is the default for newly created files and
|
161
|
-
# subdirectories.
|
162
|
-
#
|
163
|
-
def self.encrypted?(file)
|
164
|
-
check_for_attribute(file, FILE_ATTRIBUTE_ENCRYPTED)
|
165
|
-
end
|
166
|
-
|
167
|
-
# Returns whether or not the file or directory is hidden. A hidden file
|
168
|
-
# does not show up in an ordinary directory listing.
|
169
|
-
#
|
170
|
-
def self.hidden?(file)
|
171
|
-
check_for_attribute(file, FILE_ATTRIBUTE_HIDDEN)
|
172
|
-
end
|
173
|
-
|
174
|
-
# Returns whether or not the file or directory has been indexed by
|
175
|
-
# the content indexing service.
|
176
|
-
#
|
177
|
-
def self.indexed?(file)
|
178
|
-
!check_for_attribute(file, FILE_ATTRIBUTE_NOT_CONTENT_INDEXED)
|
179
|
-
end
|
180
|
-
|
181
|
-
# Returns whether or not the file is a normal file or directory. A normal
|
182
|
-
# file or directory does not have any other attributes set.
|
183
|
-
#
|
184
|
-
def self.normal?(file)
|
185
|
-
check_for_attribute(file, FILE_ATTRIBUTE_NORMAL)
|
186
|
-
end
|
187
|
-
|
188
|
-
# Returns whether or not the data of a file is available immediately.
|
189
|
-
# If true it indicates that the file data is physically moved to offline
|
190
|
-
# storage.
|
191
|
-
#
|
192
|
-
def self.offline?(file)
|
193
|
-
check_for_attribute(file, FILE_ATTRIBUTE_OFFLINE)
|
194
|
-
end
|
195
|
-
|
196
|
-
# Returns whether or not the file is read-only. If a file is read-only then
|
197
|
-
# applications can read the file, but cannot write to it or delete it.
|
198
|
-
#
|
199
|
-
# Note that this attribute is not honored on directories.
|
200
|
-
#
|
201
|
-
def self.readonly?(file)
|
202
|
-
check_for_attribute(file, FILE_ATTRIBUTE_READONLY)
|
203
|
-
end
|
204
|
-
|
205
|
-
# Returns true if the file or directory has an associated reparse point. A
|
206
|
-
# reparse point is a collection of user defined data associated with a file
|
207
|
-
# or directory. For more on reparse points, search
|
208
|
-
# http://msdn.microsoft.com.
|
209
|
-
#
|
210
|
-
def self.reparse_point?(file)
|
211
|
-
check_for_attribute(file, FILE_ATTRIBUTE_REPARSE_POINT)
|
212
|
-
end
|
213
|
-
|
214
|
-
# Returns whether or not the file is a sparse file. A sparse file is a
|
215
|
-
# file in which much of the data is zeros, typically image files.
|
216
|
-
#
|
217
|
-
def self.sparse?(file)
|
218
|
-
check_for_attribute(file, FILE_ATTRIBUTE_SPARSE_FILE)
|
219
|
-
end
|
220
|
-
|
221
|
-
# Returns whether or not the file or directory is a system file. A system
|
222
|
-
# file is a file that is part of the operating system or is used exclusively
|
223
|
-
# by the operating system.
|
224
|
-
#
|
225
|
-
def self.system?(file)
|
226
|
-
check_for_attribute(file, FILE_ATTRIBUTE_SYSTEM)
|
227
|
-
end
|
228
|
-
|
229
|
-
# Returns whether or not the file is being used for temporary storage.
|
230
|
-
#
|
231
|
-
# File systems avoid writing data back to mass storage if sufficient cache
|
232
|
-
# memory is available, because often the application deletes the temporary
|
233
|
-
# file shortly after the handle is closed. In that case, the system can
|
234
|
-
# entirely avoid writing the data. Otherwise, the data will be written after
|
235
|
-
# the handle is closed.
|
236
|
-
#
|
237
|
-
def self.temporary?(file)
|
238
|
-
check_for_attribute(file, FILE_ATTRIBUTE_TEMPORARY)
|
239
|
-
end
|
240
|
-
|
241
|
-
class << self
|
242
|
-
alias read_only? readonly?
|
243
|
-
alias content_indexed? indexed?
|
244
|
-
alias set_attr set_attributes
|
245
|
-
alias unset_attr remove_attributes
|
246
|
-
end
|
247
|
-
|
248
|
-
## INSTANCE METHODS
|
249
|
-
|
250
|
-
# Sets whether or not the file is an archive file. Applications typically
|
251
|
-
# use this attribute to mark files for backup or removal.
|
252
|
-
#
|
253
|
-
def archive=(bool)
|
254
|
-
wide_path = self.path.wincode
|
255
|
-
attributes = GetFileAttributesW(wide_path)
|
256
|
-
|
257
|
-
if attributes == INVALID_FILE_ATTRIBUTES
|
258
|
-
raise SystemCallError.new("GetFileAttributes", FFI.errno)
|
259
|
-
end
|
260
|
-
|
261
|
-
if bool
|
262
|
-
attributes |= FILE_ATTRIBUTE_ARCHIVE;
|
263
|
-
else
|
264
|
-
attributes &= ~FILE_ATTRIBUTE_ARCHIVE;
|
265
|
-
end
|
266
|
-
|
267
|
-
unless SetFileAttributesW(wide_path, attributes)
|
268
|
-
raise SystemCallError.new("SetFileAttributes", FFI.errno)
|
269
|
-
end
|
270
|
-
|
271
|
-
self
|
272
|
-
end
|
273
|
-
|
274
|
-
# Sets whether or not the file is a compressed file. For a file, this means
|
275
|
-
# that all of the data in the file is compressed. For a directory, this means
|
276
|
-
# that compression is the default for newly created files and subdirectories.
|
277
|
-
#
|
278
|
-
def compressed=(bool)
|
279
|
-
# We can't use get_osfhandle here because we need specific attributes
|
280
|
-
handle = CreateFileW(
|
281
|
-
self.path.wincode,
|
282
|
-
FILE_READ_DATA | FILE_WRITE_DATA,
|
283
|
-
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
284
|
-
nil,
|
285
|
-
OPEN_EXISTING,
|
286
|
-
0,
|
287
|
-
0
|
288
|
-
)
|
289
|
-
|
290
|
-
if handle == INVALID_HANDLE_VALUE
|
291
|
-
raise SystemCallError.new("CreateFile", FFI.errno)
|
292
|
-
end
|
293
|
-
|
294
|
-
in_buf = FFI::MemoryPointer.new(:ulong)
|
295
|
-
bytes = FFI::MemoryPointer.new(:ulong)
|
296
|
-
|
297
|
-
compression_value = bool ? COMPRESSION_FORMAT_DEFAULT : COMPRESSION_FORMAT_NONE
|
298
|
-
in_buf.write_ulong(compression_value)
|
299
|
-
|
300
|
-
begin
|
301
|
-
bool = DeviceIoControl(
|
302
|
-
handle,
|
303
|
-
FSCTL_SET_COMPRESSION(),
|
304
|
-
in_buf,
|
305
|
-
in_buf.size,
|
306
|
-
nil,
|
307
|
-
0,
|
308
|
-
bytes,
|
309
|
-
nil
|
310
|
-
)
|
311
|
-
|
312
|
-
unless bool
|
313
|
-
raise SystemCallError.new("DeviceIoControl", FFI.errno)
|
314
|
-
end
|
315
|
-
ensure
|
316
|
-
CloseHandle(handle)
|
317
|
-
in_buf.free
|
318
|
-
bytes.free
|
319
|
-
end
|
320
|
-
|
321
|
-
self
|
322
|
-
end
|
323
|
-
|
324
|
-
# Sets the hidden attribute to true or false. Setting this attribute to
|
325
|
-
# true means that the file is not included in an ordinary directory listing.
|
326
|
-
#
|
327
|
-
def hidden=(bool)
|
328
|
-
wide_path = self.path.wincode
|
329
|
-
attributes = GetFileAttributesW(wide_path)
|
330
|
-
|
331
|
-
if attributes == INVALID_FILE_ATTRIBUTES
|
332
|
-
raise SystemCallError.new("GetFileAttributes", FFI.errno)
|
333
|
-
end
|
334
|
-
|
335
|
-
if bool
|
336
|
-
attributes |= FILE_ATTRIBUTE_HIDDEN;
|
337
|
-
else
|
338
|
-
attributes &= ~FILE_ATTRIBUTE_HIDDEN;
|
339
|
-
end
|
340
|
-
|
341
|
-
unless SetFileAttributesW(wide_path, attributes)
|
342
|
-
raise SystemCallError.new("SetFileAttributes", FFI.errno)
|
343
|
-
end
|
344
|
-
|
345
|
-
self
|
346
|
-
end
|
347
|
-
|
348
|
-
# Sets the 'indexed' attribute to true or false. Setting this to
|
349
|
-
# false means that the file will not be indexed by the content indexing
|
350
|
-
# service.
|
351
|
-
#
|
352
|
-
def indexed=(bool)
|
353
|
-
wide_path = self.path.wincode
|
354
|
-
attributes = GetFileAttributesW(wide_path)
|
355
|
-
|
356
|
-
if attributes == INVALID_FILE_ATTRIBUTES
|
357
|
-
raise SystemCallError.new("GetFileAttributes", FFI.errno)
|
358
|
-
end
|
359
|
-
|
360
|
-
if bool
|
361
|
-
attributes &= ~FILE_ATTRIBUTE_NOT_CONTENT_INDEXED;
|
362
|
-
else
|
363
|
-
attributes |= FILE_ATTRIBUTE_NOT_CONTENT_INDEXED;
|
364
|
-
end
|
365
|
-
|
366
|
-
unless SetFileAttributesW(wide_path, attributes)
|
367
|
-
raise SystemCallError.new("SetFileAttributes", FFI.errno)
|
368
|
-
end
|
369
|
-
|
370
|
-
self
|
371
|
-
end
|
372
|
-
|
373
|
-
alias :content_indexed= :indexed=
|
374
|
-
|
375
|
-
# Sets the normal attribute. Note that only 'true' is a valid argument,
|
376
|
-
# which has the effect of removing most other attributes. Attempting to
|
377
|
-
# pass any value except true will raise an ArgumentError.
|
378
|
-
#
|
379
|
-
def normal=(bool)
|
380
|
-
unless bool
|
381
|
-
raise ArgumentError, "only 'true' may be passed as an argument"
|
382
|
-
end
|
383
|
-
|
384
|
-
unless SetFileAttributesW(self.path.wincode, FILE_ATTRIBUTE_NORMAL)
|
385
|
-
raise SystemCallError.new("SetFileAttributes", FFI.errno)
|
386
|
-
end
|
387
|
-
|
388
|
-
self
|
389
|
-
end
|
390
|
-
|
391
|
-
# Sets whether or not a file is online or not. Setting this to false means
|
392
|
-
# that the data of the file is not immediately available. This attribute
|
393
|
-
# indicates that the file data has been physically moved to offline storage.
|
394
|
-
# This attribute is used by Remote Storage, the hierarchical storage
|
395
|
-
# management software.
|
396
|
-
#
|
397
|
-
# Applications should not arbitrarily change this attribute.
|
398
|
-
#
|
399
|
-
def offline=(bool)
|
400
|
-
wide_path = self.path.wincode
|
401
|
-
attributes = GetFileAttributesW(wide_path)
|
402
|
-
|
403
|
-
if attributes == INVALID_FILE_ATTRIBUTES
|
404
|
-
raise SystemCallError.new("GetFileAttributes", FFI.errno)
|
405
|
-
end
|
406
|
-
|
407
|
-
if bool
|
408
|
-
attributes |= FILE_ATTRIBUTE_OFFLINE;
|
409
|
-
else
|
410
|
-
attributes &= ~FILE_ATTRIBUTE_OFFLINE;
|
411
|
-
end
|
412
|
-
|
413
|
-
unless SetFileAttributesW(wide_path, attributes)
|
414
|
-
raise SystemCallError.new("SetFileAttributes", FFI.errno)
|
415
|
-
end
|
416
|
-
|
417
|
-
self
|
418
|
-
end
|
419
|
-
|
420
|
-
# Sets the readonly attribute. If set to true the the file or directory is
|
421
|
-
# readonly. Applications can read the file but cannot write to it or delete
|
422
|
-
# it. In the case of a directory, applications cannot delete it.
|
423
|
-
#
|
424
|
-
def readonly=(bool)
|
425
|
-
wide_path = self.path.wincode
|
426
|
-
attributes = GetFileAttributesW(wide_path)
|
427
|
-
|
428
|
-
if attributes == INVALID_FILE_ATTRIBUTES
|
429
|
-
raise SystemCallError.new("GetFileAttributes", FFI.errno)
|
430
|
-
end
|
431
|
-
|
432
|
-
if bool
|
433
|
-
attributes |= FILE_ATTRIBUTE_READONLY;
|
434
|
-
else
|
435
|
-
attributes &= ~FILE_ATTRIBUTE_READONLY;
|
436
|
-
end
|
437
|
-
|
438
|
-
unless SetFileAttributesW(wide_path, attributes)
|
439
|
-
raise SystemCallError.new("SetFileAttributes", FFI.errno)
|
440
|
-
end
|
441
|
-
|
442
|
-
self
|
443
|
-
end
|
444
|
-
|
445
|
-
# Sets the file to a sparse (usually image) file. Note that you cannot
|
446
|
-
# remove the sparse property from a file.
|
447
|
-
#
|
448
|
-
def sparse=(bool)
|
449
|
-
unless bool
|
450
|
-
warn 'cannot remove sparse property from a file - operation ignored'
|
451
|
-
return
|
452
|
-
end
|
453
|
-
|
454
|
-
handle = CreateFileW(
|
455
|
-
self.path.wincode,
|
456
|
-
FILE_READ_DATA | FILE_WRITE_DATA,
|
457
|
-
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
458
|
-
0,
|
459
|
-
OPEN_EXISTING,
|
460
|
-
FSCTL_SET_SPARSE(),
|
461
|
-
0
|
462
|
-
)
|
463
|
-
|
464
|
-
if handle == INVALID_HANDLE_VALUE
|
465
|
-
raise SystemCallError.new("CreateFile", FFI.errno)
|
466
|
-
end
|
467
|
-
|
468
|
-
bytes = FFI::MemoryPointer.new(:ulong)
|
469
|
-
|
470
|
-
begin
|
471
|
-
bool = DeviceIoControl(
|
472
|
-
handle,
|
473
|
-
FSCTL_SET_SPARSE(),
|
474
|
-
nil,
|
475
|
-
0,
|
476
|
-
nil,
|
477
|
-
0,
|
478
|
-
bytes,
|
479
|
-
nil
|
480
|
-
)
|
481
|
-
|
482
|
-
unless bool == 0
|
483
|
-
raise SystemCallError.new("DeviceIoControl", FFI.errno)
|
484
|
-
end
|
485
|
-
ensure
|
486
|
-
CloseHandle(handle)
|
487
|
-
bytes.free
|
488
|
-
end
|
489
|
-
|
490
|
-
self
|
491
|
-
end
|
492
|
-
|
493
|
-
# Set whether or not the file is a system file. A system file is a file
|
494
|
-
# that is part of the operating system or is used exclusively by it.
|
495
|
-
#
|
496
|
-
def system=(bool)
|
497
|
-
wide_path = self.path.wincode
|
498
|
-
attributes = GetFileAttributesW(wide_path)
|
499
|
-
|
500
|
-
if attributes == INVALID_FILE_ATTRIBUTES
|
501
|
-
raise SystemCallError.new("GetFileAttributes", FFI.errno)
|
502
|
-
end
|
503
|
-
|
504
|
-
if bool
|
505
|
-
attributes |= FILE_ATTRIBUTE_SYSTEM;
|
506
|
-
else
|
507
|
-
attributes &= ~FILE_ATTRIBUTE_SYSTEM;
|
508
|
-
end
|
509
|
-
|
510
|
-
unless SetFileAttributesW(wide_path, attributes)
|
511
|
-
raise SystemCallError.new("SetFileAttributes", FFI.errno)
|
512
|
-
end
|
513
|
-
|
514
|
-
self
|
515
|
-
end
|
516
|
-
|
517
|
-
# Sets whether or not the file is being used for temporary storage.
|
518
|
-
#
|
519
|
-
# File systems avoid writing data back to mass storage if sufficient cache
|
520
|
-
# memory is available, because often the application deletes the temporary
|
521
|
-
# file shortly after the handle is closed. In that case, the system can
|
522
|
-
# entirely avoid writing the data. Otherwise, the data will be written
|
523
|
-
# after the handle is closed.
|
524
|
-
#
|
525
|
-
def temporary=(bool)
|
526
|
-
wide_path = self.path.wincode
|
527
|
-
attributes = GetFileAttributesW(wide_path)
|
528
|
-
|
529
|
-
if attributes == INVALID_FILE_ATTRIBUTES
|
530
|
-
raise SystemCallError.new("GetFileAttributes", FFI.errno)
|
531
|
-
end
|
532
|
-
|
533
|
-
if bool
|
534
|
-
attributes |= FILE_ATTRIBUTE_TEMPORARY;
|
535
|
-
else
|
536
|
-
attributes &= ~FILE_ATTRIBUTE_TEMPORARY;
|
537
|
-
end
|
538
|
-
|
539
|
-
unless SetFileAttributesW(wide_path, attributes)
|
540
|
-
raise SystemCallError.new("SetFileAttributes", FFI.errno)
|
541
|
-
end
|
542
|
-
|
543
|
-
self
|
544
|
-
end
|
545
|
-
|
546
|
-
private
|
547
|
-
|
548
|
-
# Used to simulate Ruby's allowance for objects that implement to_str or to_path.
|
549
|
-
#
|
550
|
-
def self.string_check(arg)
|
551
|
-
return arg if arg.is_a?(String)
|
552
|
-
return arg.send(:to_str) if arg.respond_to?(:to_str, true)
|
553
|
-
return arg.to_path if arg.respond_to?(:to_path)
|
554
|
-
raise TypeError
|
555
|
-
end
|
556
|
-
|
557
|
-
# Convenience method used internally for the various boolean singleton methods.
|
558
|
-
#
|
559
|
-
def self.check_for_attribute(file, attribute)
|
560
|
-
file = string_check(file)
|
561
|
-
attributes = GetFileAttributesW(file.wincode)
|
562
|
-
|
563
|
-
if attributes == INVALID_FILE_ATTRIBUTES
|
564
|
-
raise SystemCallError.new("GetFileAttributes", FFI.errno)
|
565
|
-
end
|
566
|
-
|
567
|
-
attributes & attribute > 0 ? true : false
|
568
|
-
end
|
569
|
-
end
|
1
|
+
require_relative 'windows/constants'
|
2
|
+
require_relative 'windows/structs'
|
3
|
+
require_relative 'windows/functions'
|
4
|
+
|
5
|
+
class File
|
6
|
+
include Windows::File::Constants
|
7
|
+
include Windows::File::Functions
|
8
|
+
extend Windows::File::Constants
|
9
|
+
extend Windows::File::Structs
|
10
|
+
extend Windows::File::Functions
|
11
|
+
|
12
|
+
# The version of the win32-file library
|
13
|
+
WIN32_FILE_ATTRIBUTE_VERSION = '1.0.4'
|
14
|
+
|
15
|
+
## ABBREVIATED ATTRIBUTE CONSTANTS
|
16
|
+
|
17
|
+
# The file or directory is an archive. Typically used to mark files for
|
18
|
+
# backup or removal.
|
19
|
+
ARCHIVE = FILE_ATTRIBUTE_ARCHIVE
|
20
|
+
|
21
|
+
# The file or directory is encrypted. For a file, this means that all
|
22
|
+
# data in the file is encrypted. For a directory, this means that
|
23
|
+
# encryption is # the default for newly created files and subdirectories.
|
24
|
+
COMPRESSED = FILE_ATTRIBUTE_COMPRESSED
|
25
|
+
|
26
|
+
# The file is hidden. Not included in an ordinary directory listing.
|
27
|
+
HIDDEN = FILE_ATTRIBUTE_HIDDEN
|
28
|
+
|
29
|
+
# A file that does not have any other attributes set.
|
30
|
+
NORMAL = FILE_ATTRIBUTE_NORMAL
|
31
|
+
|
32
|
+
# The data of a file is not immediately available. This attribute indicates
|
33
|
+
# that file data is physically moved to offline storage.
|
34
|
+
OFFLINE = FILE_ATTRIBUTE_OFFLINE
|
35
|
+
|
36
|
+
# The file is read only. Apps can read it, but not write to it or delete it.
|
37
|
+
READONLY = FILE_ATTRIBUTE_READONLY
|
38
|
+
|
39
|
+
# The file is part of or used exclusively by an operating system.
|
40
|
+
SYSTEM = FILE_ATTRIBUTE_SYSTEM
|
41
|
+
|
42
|
+
# The file is being used for temporary storage.
|
43
|
+
TEMPORARY = FILE_ATTRIBUTE_TEMPORARY
|
44
|
+
|
45
|
+
# The file or directory is to be indexed by the content indexing service.
|
46
|
+
# Note that we have inverted the traditional definition.
|
47
|
+
INDEXED = 0x0002000
|
48
|
+
|
49
|
+
# Synonym for File::INDEXED.
|
50
|
+
CONTENT_INDEXED = INDEXED
|
51
|
+
|
52
|
+
## SINGLETON METHODS
|
53
|
+
|
54
|
+
# Returns an array of strings indicating the attributes for that file.
|
55
|
+
# The possible values are:
|
56
|
+
#
|
57
|
+
# archive
|
58
|
+
# compressed
|
59
|
+
# directory
|
60
|
+
# encrypted
|
61
|
+
# hidden
|
62
|
+
# indexed
|
63
|
+
# normal
|
64
|
+
# offline
|
65
|
+
# readonly
|
66
|
+
# reparse_point
|
67
|
+
# sparse
|
68
|
+
# system
|
69
|
+
# temporary
|
70
|
+
#
|
71
|
+
def self.attributes(file)
|
72
|
+
attributes = GetFileAttributesW(string_check(file).wincode)
|
73
|
+
|
74
|
+
if attributes == INVALID_FILE_ATTRIBUTES
|
75
|
+
raise SystemCallError.new("GetFileAttributes", FFI.errno)
|
76
|
+
end
|
77
|
+
|
78
|
+
arr = []
|
79
|
+
|
80
|
+
arr << 'archive' if attributes & FILE_ATTRIBUTE_ARCHIVE > 0
|
81
|
+
arr << 'compressed' if attributes & FILE_ATTRIBUTE_COMPRESSED > 0
|
82
|
+
arr << 'directory' if attributes & FILE_ATTRIBUTE_DIRECTORY > 0
|
83
|
+
arr << 'encrypted' if attributes & FILE_ATTRIBUTE_ENCRYPTED > 0
|
84
|
+
arr << 'hidden' if attributes & FILE_ATTRIBUTE_HIDDEN > 0
|
85
|
+
arr << 'indexed' if attributes & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED == 0
|
86
|
+
arr << 'normal' if attributes & FILE_ATTRIBUTE_NORMAL > 0
|
87
|
+
arr << 'offline' if attributes & FILE_ATTRIBUTE_OFFLINE > 0
|
88
|
+
arr << 'readonly' if attributes & FILE_ATTRIBUTE_READONLY > 0
|
89
|
+
arr << 'reparse_point' if attributes & FILE_ATTRIBUTE_REPARSE_POINT > 0
|
90
|
+
arr << 'sparse' if attributes & FILE_ATTRIBUTE_SPARSE_FILE > 0
|
91
|
+
arr << 'system' if attributes & FILE_ATTRIBUTE_SYSTEM > 0
|
92
|
+
arr << 'temporary' if attributes & FILE_ATTRIBUTE_TEMPORARY > 0
|
93
|
+
|
94
|
+
arr
|
95
|
+
end
|
96
|
+
|
97
|
+
# Sets the file attributes based on the given (numeric) +flags+. This does
|
98
|
+
# not remove existing attributes, it merely adds to them. Use the
|
99
|
+
# File.remove_attributes method if you want to remove them.
|
100
|
+
#
|
101
|
+
# Please not that certain attributes cannot always be applied. For example,
|
102
|
+
# you cannot convert a regular file into a directory. Common sense should
|
103
|
+
# guide you here.
|
104
|
+
#
|
105
|
+
def self.set_attributes(file, flags)
|
106
|
+
wfile = string_check(file).wincode
|
107
|
+
attributes = GetFileAttributesW(wfile)
|
108
|
+
|
109
|
+
if attributes == INVALID_FILE_ATTRIBUTES
|
110
|
+
raise SystemCallError.new("GetFileAttributes", FFI.errno)
|
111
|
+
end
|
112
|
+
|
113
|
+
attributes |= flags
|
114
|
+
|
115
|
+
unless SetFileAttributesW(wfile, attributes)
|
116
|
+
raise SystemCallError.new("SetFileAttributes", FFI.errno)
|
117
|
+
end
|
118
|
+
|
119
|
+
self
|
120
|
+
end
|
121
|
+
|
122
|
+
# Removes the file attributes based on the given (numeric) +flags+.
|
123
|
+
#
|
124
|
+
def self.remove_attributes(file, flags)
|
125
|
+
wfile = string_check(file).wincode
|
126
|
+
attributes = GetFileAttributesW(wfile)
|
127
|
+
|
128
|
+
if attributes == INVALID_FILE_ATTRIBUTES
|
129
|
+
raise SystemCallError.new("GetFileAttributes", FFI.errno)
|
130
|
+
end
|
131
|
+
|
132
|
+
attributes &= ~flags
|
133
|
+
|
134
|
+
unless SetFileAttributesW(wfile, attributes)
|
135
|
+
raise SystemCallError.new("SetFileAttributes", FFI.errno)
|
136
|
+
end
|
137
|
+
|
138
|
+
self
|
139
|
+
end
|
140
|
+
|
141
|
+
# Returns whether or not the file or directory is an archive file or
|
142
|
+
# directory. Applications typically use this attribute to mark files
|
143
|
+
# for backup or removal.
|
144
|
+
#
|
145
|
+
def self.archive?(file)
|
146
|
+
check_for_attribute(file, FILE_ATTRIBUTE_ARCHIVE)
|
147
|
+
end
|
148
|
+
|
149
|
+
# Returns whether or not the file or directory is compressed. For a file,
|
150
|
+
# this means that all of the data in the file is compressed. For a directory,
|
151
|
+
# this means that compression is the default for newly created files and
|
152
|
+
# subdirectories.
|
153
|
+
#
|
154
|
+
def self.compressed?(file)
|
155
|
+
check_for_attribute(file, FILE_ATTRIBUTE_COMPRESSED)
|
156
|
+
end
|
157
|
+
|
158
|
+
# Returns whether or not the file or directory is encrypted. For a file,
|
159
|
+
# this means that all data in the file is encrypted. For a directory, this
|
160
|
+
# means that encryption is the default for newly created files and
|
161
|
+
# subdirectories.
|
162
|
+
#
|
163
|
+
def self.encrypted?(file)
|
164
|
+
check_for_attribute(file, FILE_ATTRIBUTE_ENCRYPTED)
|
165
|
+
end
|
166
|
+
|
167
|
+
# Returns whether or not the file or directory is hidden. A hidden file
|
168
|
+
# does not show up in an ordinary directory listing.
|
169
|
+
#
|
170
|
+
def self.hidden?(file)
|
171
|
+
check_for_attribute(file, FILE_ATTRIBUTE_HIDDEN)
|
172
|
+
end
|
173
|
+
|
174
|
+
# Returns whether or not the file or directory has been indexed by
|
175
|
+
# the content indexing service.
|
176
|
+
#
|
177
|
+
def self.indexed?(file)
|
178
|
+
!check_for_attribute(file, FILE_ATTRIBUTE_NOT_CONTENT_INDEXED)
|
179
|
+
end
|
180
|
+
|
181
|
+
# Returns whether or not the file is a normal file or directory. A normal
|
182
|
+
# file or directory does not have any other attributes set.
|
183
|
+
#
|
184
|
+
def self.normal?(file)
|
185
|
+
check_for_attribute(file, FILE_ATTRIBUTE_NORMAL)
|
186
|
+
end
|
187
|
+
|
188
|
+
# Returns whether or not the data of a file is available immediately.
|
189
|
+
# If true it indicates that the file data is physically moved to offline
|
190
|
+
# storage.
|
191
|
+
#
|
192
|
+
def self.offline?(file)
|
193
|
+
check_for_attribute(file, FILE_ATTRIBUTE_OFFLINE)
|
194
|
+
end
|
195
|
+
|
196
|
+
# Returns whether or not the file is read-only. If a file is read-only then
|
197
|
+
# applications can read the file, but cannot write to it or delete it.
|
198
|
+
#
|
199
|
+
# Note that this attribute is not honored on directories.
|
200
|
+
#
|
201
|
+
def self.readonly?(file)
|
202
|
+
check_for_attribute(file, FILE_ATTRIBUTE_READONLY)
|
203
|
+
end
|
204
|
+
|
205
|
+
# Returns true if the file or directory has an associated reparse point. A
|
206
|
+
# reparse point is a collection of user defined data associated with a file
|
207
|
+
# or directory. For more on reparse points, search
|
208
|
+
# http://msdn.microsoft.com.
|
209
|
+
#
|
210
|
+
def self.reparse_point?(file)
|
211
|
+
check_for_attribute(file, FILE_ATTRIBUTE_REPARSE_POINT)
|
212
|
+
end
|
213
|
+
|
214
|
+
# Returns whether or not the file is a sparse file. A sparse file is a
|
215
|
+
# file in which much of the data is zeros, typically image files.
|
216
|
+
#
|
217
|
+
def self.sparse?(file)
|
218
|
+
check_for_attribute(file, FILE_ATTRIBUTE_SPARSE_FILE)
|
219
|
+
end
|
220
|
+
|
221
|
+
# Returns whether or not the file or directory is a system file. A system
|
222
|
+
# file is a file that is part of the operating system or is used exclusively
|
223
|
+
# by the operating system.
|
224
|
+
#
|
225
|
+
def self.system?(file)
|
226
|
+
check_for_attribute(file, FILE_ATTRIBUTE_SYSTEM)
|
227
|
+
end
|
228
|
+
|
229
|
+
# Returns whether or not the file is being used for temporary storage.
|
230
|
+
#
|
231
|
+
# File systems avoid writing data back to mass storage if sufficient cache
|
232
|
+
# memory is available, because often the application deletes the temporary
|
233
|
+
# file shortly after the handle is closed. In that case, the system can
|
234
|
+
# entirely avoid writing the data. Otherwise, the data will be written after
|
235
|
+
# the handle is closed.
|
236
|
+
#
|
237
|
+
def self.temporary?(file)
|
238
|
+
check_for_attribute(file, FILE_ATTRIBUTE_TEMPORARY)
|
239
|
+
end
|
240
|
+
|
241
|
+
class << self
|
242
|
+
alias read_only? readonly?
|
243
|
+
alias content_indexed? indexed?
|
244
|
+
alias set_attr set_attributes
|
245
|
+
alias unset_attr remove_attributes
|
246
|
+
end
|
247
|
+
|
248
|
+
## INSTANCE METHODS
|
249
|
+
|
250
|
+
# Sets whether or not the file is an archive file. Applications typically
|
251
|
+
# use this attribute to mark files for backup or removal.
|
252
|
+
#
|
253
|
+
def archive=(bool)
|
254
|
+
wide_path = self.path.wincode
|
255
|
+
attributes = GetFileAttributesW(wide_path)
|
256
|
+
|
257
|
+
if attributes == INVALID_FILE_ATTRIBUTES
|
258
|
+
raise SystemCallError.new("GetFileAttributes", FFI.errno)
|
259
|
+
end
|
260
|
+
|
261
|
+
if bool
|
262
|
+
attributes |= FILE_ATTRIBUTE_ARCHIVE;
|
263
|
+
else
|
264
|
+
attributes &= ~FILE_ATTRIBUTE_ARCHIVE;
|
265
|
+
end
|
266
|
+
|
267
|
+
unless SetFileAttributesW(wide_path, attributes)
|
268
|
+
raise SystemCallError.new("SetFileAttributes", FFI.errno)
|
269
|
+
end
|
270
|
+
|
271
|
+
self
|
272
|
+
end
|
273
|
+
|
274
|
+
# Sets whether or not the file is a compressed file. For a file, this means
|
275
|
+
# that all of the data in the file is compressed. For a directory, this means
|
276
|
+
# that compression is the default for newly created files and subdirectories.
|
277
|
+
#
|
278
|
+
def compressed=(bool)
|
279
|
+
# We can't use get_osfhandle here because we need specific attributes
|
280
|
+
handle = CreateFileW(
|
281
|
+
self.path.wincode,
|
282
|
+
FILE_READ_DATA | FILE_WRITE_DATA,
|
283
|
+
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
284
|
+
nil,
|
285
|
+
OPEN_EXISTING,
|
286
|
+
0,
|
287
|
+
0
|
288
|
+
)
|
289
|
+
|
290
|
+
if handle == INVALID_HANDLE_VALUE
|
291
|
+
raise SystemCallError.new("CreateFile", FFI.errno)
|
292
|
+
end
|
293
|
+
|
294
|
+
in_buf = FFI::MemoryPointer.new(:ulong)
|
295
|
+
bytes = FFI::MemoryPointer.new(:ulong)
|
296
|
+
|
297
|
+
compression_value = bool ? COMPRESSION_FORMAT_DEFAULT : COMPRESSION_FORMAT_NONE
|
298
|
+
in_buf.write_ulong(compression_value)
|
299
|
+
|
300
|
+
begin
|
301
|
+
bool = DeviceIoControl(
|
302
|
+
handle,
|
303
|
+
FSCTL_SET_COMPRESSION(),
|
304
|
+
in_buf,
|
305
|
+
in_buf.size,
|
306
|
+
nil,
|
307
|
+
0,
|
308
|
+
bytes,
|
309
|
+
nil
|
310
|
+
)
|
311
|
+
|
312
|
+
unless bool
|
313
|
+
raise SystemCallError.new("DeviceIoControl", FFI.errno)
|
314
|
+
end
|
315
|
+
ensure
|
316
|
+
CloseHandle(handle)
|
317
|
+
in_buf.free
|
318
|
+
bytes.free
|
319
|
+
end
|
320
|
+
|
321
|
+
self
|
322
|
+
end
|
323
|
+
|
324
|
+
# Sets the hidden attribute to true or false. Setting this attribute to
|
325
|
+
# true means that the file is not included in an ordinary directory listing.
|
326
|
+
#
|
327
|
+
def hidden=(bool)
|
328
|
+
wide_path = self.path.wincode
|
329
|
+
attributes = GetFileAttributesW(wide_path)
|
330
|
+
|
331
|
+
if attributes == INVALID_FILE_ATTRIBUTES
|
332
|
+
raise SystemCallError.new("GetFileAttributes", FFI.errno)
|
333
|
+
end
|
334
|
+
|
335
|
+
if bool
|
336
|
+
attributes |= FILE_ATTRIBUTE_HIDDEN;
|
337
|
+
else
|
338
|
+
attributes &= ~FILE_ATTRIBUTE_HIDDEN;
|
339
|
+
end
|
340
|
+
|
341
|
+
unless SetFileAttributesW(wide_path, attributes)
|
342
|
+
raise SystemCallError.new("SetFileAttributes", FFI.errno)
|
343
|
+
end
|
344
|
+
|
345
|
+
self
|
346
|
+
end
|
347
|
+
|
348
|
+
# Sets the 'indexed' attribute to true or false. Setting this to
|
349
|
+
# false means that the file will not be indexed by the content indexing
|
350
|
+
# service.
|
351
|
+
#
|
352
|
+
def indexed=(bool)
|
353
|
+
wide_path = self.path.wincode
|
354
|
+
attributes = GetFileAttributesW(wide_path)
|
355
|
+
|
356
|
+
if attributes == INVALID_FILE_ATTRIBUTES
|
357
|
+
raise SystemCallError.new("GetFileAttributes", FFI.errno)
|
358
|
+
end
|
359
|
+
|
360
|
+
if bool
|
361
|
+
attributes &= ~FILE_ATTRIBUTE_NOT_CONTENT_INDEXED;
|
362
|
+
else
|
363
|
+
attributes |= FILE_ATTRIBUTE_NOT_CONTENT_INDEXED;
|
364
|
+
end
|
365
|
+
|
366
|
+
unless SetFileAttributesW(wide_path, attributes)
|
367
|
+
raise SystemCallError.new("SetFileAttributes", FFI.errno)
|
368
|
+
end
|
369
|
+
|
370
|
+
self
|
371
|
+
end
|
372
|
+
|
373
|
+
alias :content_indexed= :indexed=
|
374
|
+
|
375
|
+
# Sets the normal attribute. Note that only 'true' is a valid argument,
|
376
|
+
# which has the effect of removing most other attributes. Attempting to
|
377
|
+
# pass any value except true will raise an ArgumentError.
|
378
|
+
#
|
379
|
+
def normal=(bool)
|
380
|
+
unless bool
|
381
|
+
raise ArgumentError, "only 'true' may be passed as an argument"
|
382
|
+
end
|
383
|
+
|
384
|
+
unless SetFileAttributesW(self.path.wincode, FILE_ATTRIBUTE_NORMAL)
|
385
|
+
raise SystemCallError.new("SetFileAttributes", FFI.errno)
|
386
|
+
end
|
387
|
+
|
388
|
+
self
|
389
|
+
end
|
390
|
+
|
391
|
+
# Sets whether or not a file is online or not. Setting this to false means
|
392
|
+
# that the data of the file is not immediately available. This attribute
|
393
|
+
# indicates that the file data has been physically moved to offline storage.
|
394
|
+
# This attribute is used by Remote Storage, the hierarchical storage
|
395
|
+
# management software.
|
396
|
+
#
|
397
|
+
# Applications should not arbitrarily change this attribute.
|
398
|
+
#
|
399
|
+
def offline=(bool)
|
400
|
+
wide_path = self.path.wincode
|
401
|
+
attributes = GetFileAttributesW(wide_path)
|
402
|
+
|
403
|
+
if attributes == INVALID_FILE_ATTRIBUTES
|
404
|
+
raise SystemCallError.new("GetFileAttributes", FFI.errno)
|
405
|
+
end
|
406
|
+
|
407
|
+
if bool
|
408
|
+
attributes |= FILE_ATTRIBUTE_OFFLINE;
|
409
|
+
else
|
410
|
+
attributes &= ~FILE_ATTRIBUTE_OFFLINE;
|
411
|
+
end
|
412
|
+
|
413
|
+
unless SetFileAttributesW(wide_path, attributes)
|
414
|
+
raise SystemCallError.new("SetFileAttributes", FFI.errno)
|
415
|
+
end
|
416
|
+
|
417
|
+
self
|
418
|
+
end
|
419
|
+
|
420
|
+
# Sets the readonly attribute. If set to true the the file or directory is
|
421
|
+
# readonly. Applications can read the file but cannot write to it or delete
|
422
|
+
# it. In the case of a directory, applications cannot delete it.
|
423
|
+
#
|
424
|
+
def readonly=(bool)
|
425
|
+
wide_path = self.path.wincode
|
426
|
+
attributes = GetFileAttributesW(wide_path)
|
427
|
+
|
428
|
+
if attributes == INVALID_FILE_ATTRIBUTES
|
429
|
+
raise SystemCallError.new("GetFileAttributes", FFI.errno)
|
430
|
+
end
|
431
|
+
|
432
|
+
if bool
|
433
|
+
attributes |= FILE_ATTRIBUTE_READONLY;
|
434
|
+
else
|
435
|
+
attributes &= ~FILE_ATTRIBUTE_READONLY;
|
436
|
+
end
|
437
|
+
|
438
|
+
unless SetFileAttributesW(wide_path, attributes)
|
439
|
+
raise SystemCallError.new("SetFileAttributes", FFI.errno)
|
440
|
+
end
|
441
|
+
|
442
|
+
self
|
443
|
+
end
|
444
|
+
|
445
|
+
# Sets the file to a sparse (usually image) file. Note that you cannot
|
446
|
+
# remove the sparse property from a file.
|
447
|
+
#
|
448
|
+
def sparse=(bool)
|
449
|
+
unless bool
|
450
|
+
warn 'cannot remove sparse property from a file - operation ignored'
|
451
|
+
return
|
452
|
+
end
|
453
|
+
|
454
|
+
handle = CreateFileW(
|
455
|
+
self.path.wincode,
|
456
|
+
FILE_READ_DATA | FILE_WRITE_DATA,
|
457
|
+
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
458
|
+
0,
|
459
|
+
OPEN_EXISTING,
|
460
|
+
FSCTL_SET_SPARSE(),
|
461
|
+
0
|
462
|
+
)
|
463
|
+
|
464
|
+
if handle == INVALID_HANDLE_VALUE
|
465
|
+
raise SystemCallError.new("CreateFile", FFI.errno)
|
466
|
+
end
|
467
|
+
|
468
|
+
bytes = FFI::MemoryPointer.new(:ulong)
|
469
|
+
|
470
|
+
begin
|
471
|
+
bool = DeviceIoControl(
|
472
|
+
handle,
|
473
|
+
FSCTL_SET_SPARSE(),
|
474
|
+
nil,
|
475
|
+
0,
|
476
|
+
nil,
|
477
|
+
0,
|
478
|
+
bytes,
|
479
|
+
nil
|
480
|
+
)
|
481
|
+
|
482
|
+
unless bool == 0
|
483
|
+
raise SystemCallError.new("DeviceIoControl", FFI.errno)
|
484
|
+
end
|
485
|
+
ensure
|
486
|
+
CloseHandle(handle)
|
487
|
+
bytes.free
|
488
|
+
end
|
489
|
+
|
490
|
+
self
|
491
|
+
end
|
492
|
+
|
493
|
+
# Set whether or not the file is a system file. A system file is a file
|
494
|
+
# that is part of the operating system or is used exclusively by it.
|
495
|
+
#
|
496
|
+
def system=(bool)
|
497
|
+
wide_path = self.path.wincode
|
498
|
+
attributes = GetFileAttributesW(wide_path)
|
499
|
+
|
500
|
+
if attributes == INVALID_FILE_ATTRIBUTES
|
501
|
+
raise SystemCallError.new("GetFileAttributes", FFI.errno)
|
502
|
+
end
|
503
|
+
|
504
|
+
if bool
|
505
|
+
attributes |= FILE_ATTRIBUTE_SYSTEM;
|
506
|
+
else
|
507
|
+
attributes &= ~FILE_ATTRIBUTE_SYSTEM;
|
508
|
+
end
|
509
|
+
|
510
|
+
unless SetFileAttributesW(wide_path, attributes)
|
511
|
+
raise SystemCallError.new("SetFileAttributes", FFI.errno)
|
512
|
+
end
|
513
|
+
|
514
|
+
self
|
515
|
+
end
|
516
|
+
|
517
|
+
# Sets whether or not the file is being used for temporary storage.
|
518
|
+
#
|
519
|
+
# File systems avoid writing data back to mass storage if sufficient cache
|
520
|
+
# memory is available, because often the application deletes the temporary
|
521
|
+
# file shortly after the handle is closed. In that case, the system can
|
522
|
+
# entirely avoid writing the data. Otherwise, the data will be written
|
523
|
+
# after the handle is closed.
|
524
|
+
#
|
525
|
+
def temporary=(bool)
|
526
|
+
wide_path = self.path.wincode
|
527
|
+
attributes = GetFileAttributesW(wide_path)
|
528
|
+
|
529
|
+
if attributes == INVALID_FILE_ATTRIBUTES
|
530
|
+
raise SystemCallError.new("GetFileAttributes", FFI.errno)
|
531
|
+
end
|
532
|
+
|
533
|
+
if bool
|
534
|
+
attributes |= FILE_ATTRIBUTE_TEMPORARY;
|
535
|
+
else
|
536
|
+
attributes &= ~FILE_ATTRIBUTE_TEMPORARY;
|
537
|
+
end
|
538
|
+
|
539
|
+
unless SetFileAttributesW(wide_path, attributes)
|
540
|
+
raise SystemCallError.new("SetFileAttributes", FFI.errno)
|
541
|
+
end
|
542
|
+
|
543
|
+
self
|
544
|
+
end
|
545
|
+
|
546
|
+
private
|
547
|
+
|
548
|
+
# Used to simulate Ruby's allowance for objects that implement to_str or to_path.
|
549
|
+
#
|
550
|
+
def self.string_check(arg)
|
551
|
+
return arg if arg.is_a?(String)
|
552
|
+
return arg.send(:to_str) if arg.respond_to?(:to_str, true)
|
553
|
+
return arg.to_path if arg.respond_to?(:to_path)
|
554
|
+
raise TypeError
|
555
|
+
end
|
556
|
+
|
557
|
+
# Convenience method used internally for the various boolean singleton methods.
|
558
|
+
#
|
559
|
+
def self.check_for_attribute(file, attribute)
|
560
|
+
file = string_check(file)
|
561
|
+
attributes = GetFileAttributesW(file.wincode)
|
562
|
+
|
563
|
+
if attributes == INVALID_FILE_ATTRIBUTES
|
564
|
+
raise SystemCallError.new("GetFileAttributes", FFI.errno)
|
565
|
+
end
|
566
|
+
|
567
|
+
attributes & attribute > 0 ? true : false
|
568
|
+
end
|
569
|
+
end
|