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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e979471f10493700b371adf06ef6ae4a78b25daa
4
- data.tar.gz: d9d23da6540f250822ab25ee9ab8cf32e1a734a2
3
+ metadata.gz: 497d0296b633a20cb8ee04f2f2367646fa7c3262
4
+ data.tar.gz: ca978fbcd970c2bf542b423efe3bfa602493a0fb
5
5
  SHA512:
6
- metadata.gz: b09b0521108e59e35a98ed8357d7cf2df2f06db138c58cc3ca16efb29ffbcc952c4ed64abd06df76f709a2ef23ed37ea76c2e300a2b927a3eaf9713bd373ca69
7
- data.tar.gz: 414fe460270f5702e118a7b6bda0e23abafb3a600d14bbc2202fa3439e929c6b48e2c21e24260243b108aa6466848cf9861490a50c28124a941dc472b7b29bfe
6
+ metadata.gz: b9f5b84a26f35e71103520b4e052cfadfacc70a5669273137b6fc5cbd3615ef6354343036f2b73d5a57b47f578816c0c5432b97f9a90a257417febca8d446906
7
+ data.tar.gz: ed6c2f906487368007d40a8f1d7465e9a3500591200c8e6b681f67aedc5f74e03cf02a2e0d550f081abe1d447ed497babb8563ccf0b2c91fdce7f590f373ef8a
Binary file
Binary file
data/CHANGES CHANGED
@@ -1,16 +1,21 @@
1
- = 1.0.3 - 8-Nov-2014
2
- * Make FFI functions private.
3
- * Removed reference to Rubyforge in gemspec.
4
- * Use require_relative internally.
5
- * Explicitly free some FFI memory pointers where appropriate.
6
-
7
- = 1.0.2 - 28-Apr-2014
8
- * The singleton methods now accept arguments that implement to_str or to_path.
9
-
10
- = 1.0.1 - 8-Apr-2013
11
- * Fixed the HANDLE prototypes in the underlying FFI code for 64 bit Ruby.
12
- * Fixed the return type for SetFileAttributes, and adjusted the code to match.
13
- * Added some tests for constants.
14
-
15
- = 1.0.0 - 26-Nov-2012
16
- * Initial release as an independent library.
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
- * lib/win32/file/attributes.rb
7
- * lib/win32/file/windows/constants.rb
8
- * lib/win32/file/windows/functions.rb
9
- * lib/win32/file/windows/structs.rb
10
- * test/test_win32_file_attributes.rb
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
- spec = eval(IO.read('win32-file-attributes.gemspec'))
11
- if Gem::VERSION >= "2.0"
12
- require 'rubygems/package'
13
- Gem::Package.build(spec)
14
- else
15
- Gem::Builder.new(spec).build
16
- end
17
- end
18
-
19
- desc "Install the win32-file-attributes gem"
20
- task :install => [:create] do
21
- file = Dir["*.gem"].first
22
- sh "gem install -l #{file}"
23
- end
24
- end
25
-
26
- Rake::TestTask.new do |t|
27
- task :test => :clean
28
- t.warning = true
29
- t.verbose = true
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.3'
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