win32-file-attributes 1.0.3 → 1.0.4

Sign up to get free protection for your applications and to get access to all the features.
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