win32-file-attributes 1.0.1 → 1.0.2

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2d64c60789cb492bfacc0ecf1381b97be3affc3c
4
+ data.tar.gz: e08e6072ea0776de56ce9a540f16b67c7aef7b73
5
+ SHA512:
6
+ metadata.gz: fc5e4cf1840d0d34cee7f697179048c734d9ada03e641da8c65fc9f5fbc21df42a16a4a924744d22a0bbaae3cfa0ea0efb67aeb17ae6160204ee5381cbf1b037
7
+ data.tar.gz: 2fbef9b336fdc7db8d837055716260ab724d153bea18b91deacbc0e37aa1ebab794ceffa09dfe2465b995ddf1290fd7addef1400648999f51f6cd28545aff4a7
data/CHANGES CHANGED
@@ -1,7 +1,10 @@
1
- = 1.0.1 - 8-Apr-2013
2
- * Fixed the HANDLE prototypes in the underlying FFI code for 64 bit Ruby.
3
- * Fixed the return type for SetFileAttributes, and adjusted the code to match.
4
- * Added some tests for constants.
5
-
6
- = 1.0.0 - 26-Nov-2012
7
- * Initial release as an independent library.
1
+ = 1.0.2 - 28-Apr-2014
2
+ * The singleton methods now accept arguments that implement to_str or to_path.
3
+
4
+ = 1.0.1 - 8-Apr-2013
5
+ * Fixed the HANDLE prototypes in the underlying FFI code for 64 bit Ruby.
6
+ * Fixed the return type for SetFileAttributes, and adjusted the code to match.
7
+ * Added some tests for constants.
8
+
9
+ = 1.0.0 - 26-Nov-2012
10
+ * Initial release as an independent library.
data/MANIFEST CHANGED
@@ -1,10 +1,10 @@
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
+ * 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
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-2012, 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,27 +1,32 @@
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
- Gem::Builder.new(spec).build
12
- end
13
-
14
- desc "Install the win32-file-attributes gem"
15
- task :install => [:create] do
16
- file = Dir["*.gem"].first
17
- sh "gem install #{file}"
18
- end
19
- end
20
-
21
- Rake::TestTask.new do |t|
22
- task :test => :clean
23
- t.warning = true
24
- t.verbose = true
25
- end
26
-
27
- 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
+ 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,556 +1,566 @@
1
- require File.join(File.dirname(__FILE__), 'windows', 'constants')
2
- require File.join(File.dirname(__FILE__), 'windows', 'structs')
3
- require File.join(File.dirname(__FILE__), '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.1'
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(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 = 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 = 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
- in_buf = FFI::MemoryPointer.new(:ulong)
280
- bytes = FFI::MemoryPointer.new(:ulong)
281
-
282
- compression_value = bool ? COMPRESSION_FORMAT_DEFAULT : COMPRESSION_FORMAT_NONE
283
- in_buf.write_ulong(compression_value)
284
-
285
- # We can't use get_osfhandle here because we need specific attributes
286
- handle = CreateFileW(
287
- self.path.wincode,
288
- FILE_READ_DATA | FILE_WRITE_DATA,
289
- FILE_SHARE_READ | FILE_SHARE_WRITE,
290
- nil,
291
- OPEN_EXISTING,
292
- 0,
293
- 0
294
- )
295
-
296
- if handle == INVALID_HANDLE_VALUE
297
- raise SystemCallError.new("CreateFile", FFI.errno)
298
- end
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
- end
318
-
319
- self
320
- end
321
-
322
- # Sets the hidden attribute to true or false. Setting this attribute to
323
- # true means that the file is not included in an ordinary directory listing.
324
- #
325
- def hidden=(bool)
326
- wide_path = self.path.wincode
327
- attributes = GetFileAttributesW(wide_path)
328
-
329
- if attributes == INVALID_FILE_ATTRIBUTES
330
- raise SystemCallError.new("GetFileAttributes", FFI.errno)
331
- end
332
-
333
- if bool
334
- attributes |= FILE_ATTRIBUTE_HIDDEN;
335
- else
336
- attributes &= ~FILE_ATTRIBUTE_HIDDEN;
337
- end
338
-
339
- unless SetFileAttributesW(wide_path, attributes)
340
- raise SystemCallError.new("SetFileAttributes", FFI.errno)
341
- end
342
-
343
- self
344
- end
345
-
346
- # Sets the 'indexed' attribute to true or false. Setting this to
347
- # false means that the file will not be indexed by the content indexing
348
- # service.
349
- #
350
- def indexed=(bool)
351
- wide_path = self.path.wincode
352
- attributes = GetFileAttributesW(wide_path)
353
-
354
- if attributes == INVALID_FILE_ATTRIBUTES
355
- raise SystemCallError.new("GetFileAttributes", FFI.errno)
356
- end
357
-
358
- if bool
359
- attributes &= ~FILE_ATTRIBUTE_NOT_CONTENT_INDEXED;
360
- else
361
- attributes |= FILE_ATTRIBUTE_NOT_CONTENT_INDEXED;
362
- end
363
-
364
- unless SetFileAttributesW(wide_path, attributes)
365
- raise SystemCallError.new("SetFileAttributes", FFI.errno)
366
- end
367
-
368
- self
369
- end
370
-
371
- alias :content_indexed= :indexed=
372
-
373
- # Sets the normal attribute. Note that only 'true' is a valid argument,
374
- # which has the effect of removing most other attributes. Attempting to
375
- # pass any value except true will raise an ArgumentError.
376
- #
377
- def normal=(bool)
378
- unless bool
379
- raise ArgumentError, "only 'true' may be passed as an argument"
380
- end
381
-
382
- unless SetFileAttributesW(self.path.wincode, FILE_ATTRIBUTE_NORMAL)
383
- raise SystemCallError.new("SetFileAttributes", FFI.errno)
384
- end
385
-
386
- self
387
- end
388
-
389
- # Sets whether or not a file is online or not. Setting this to false means
390
- # that the data of the file is not immediately available. This attribute
391
- # indicates that the file data has been physically moved to offline storage.
392
- # This attribute is used by Remote Storage, the hierarchical storage
393
- # management software.
394
- #
395
- # Applications should not arbitrarily change this attribute.
396
- #
397
- def offline=(bool)
398
- wide_path = self.path.wincode
399
- attributes = GetFileAttributesW(wide_path)
400
-
401
- if attributes == INVALID_FILE_ATTRIBUTES
402
- raise SystemCallError.new("GetFileAttributes", FFI.errno)
403
- end
404
-
405
- if bool
406
- attributes |= FILE_ATTRIBUTE_OFFLINE;
407
- else
408
- attributes &= ~FILE_ATTRIBUTE_OFFLINE;
409
- end
410
-
411
- unless SetFileAttributesW(wide_path, attributes)
412
- raise SystemCallError.new("SetFileAttributes", FFI.errno)
413
- end
414
-
415
- self
416
- end
417
-
418
- # Sets the readonly attribute. If set to true the the file or directory is
419
- # readonly. Applications can read the file but cannot write to it or delete
420
- # it. In the case of a directory, applications cannot delete it.
421
- #
422
- def readonly=(bool)
423
- wide_path = self.path.wincode
424
- attributes = GetFileAttributesW(wide_path)
425
-
426
- if attributes == INVALID_FILE_ATTRIBUTES
427
- raise SystemCallError.new("GetFileAttributes", FFI.errno)
428
- end
429
-
430
- if bool
431
- attributes |= FILE_ATTRIBUTE_READONLY;
432
- else
433
- attributes &= ~FILE_ATTRIBUTE_READONLY;
434
- end
435
-
436
- unless SetFileAttributesW(wide_path, attributes)
437
- raise SystemCallError.new("SetFileAttributes", FFI.errno)
438
- end
439
-
440
- self
441
- end
442
-
443
- # Sets the file to a sparse (usually image) file. Note that you cannot
444
- # remove the sparse property from a file.
445
- #
446
- def sparse=(bool)
447
- unless bool
448
- warn 'cannot remove sparse property from a file - operation ignored'
449
- return
450
- end
451
-
452
- bytes = FFI::MemoryPointer.new(:ulong)
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
- begin
469
- bool = DeviceIoControl(
470
- handle,
471
- FSCTL_SET_SPARSE(),
472
- nil,
473
- 0,
474
- nil,
475
- 0,
476
- bytes,
477
- nil
478
- )
479
-
480
- unless bool == 0
481
- raise SystemCallError.new("DeviceIoControl", FFI.errno)
482
- end
483
- ensure
484
- CloseHandle(handle)
485
- end
486
-
487
- self
488
- end
489
-
490
- # Set whether or not the file is a system file. A system file is a file
491
- # that is part of the operating system or is used exclusively by it.
492
- #
493
- def system=(bool)
494
- wide_path = self.path.wincode
495
- attributes = GetFileAttributesW(wide_path)
496
-
497
- if attributes == INVALID_FILE_ATTRIBUTES
498
- raise SystemCallError.new("GetFileAttributes", FFI.errno)
499
- end
500
-
501
- if bool
502
- attributes |= FILE_ATTRIBUTE_SYSTEM;
503
- else
504
- attributes &= ~FILE_ATTRIBUTE_SYSTEM;
505
- end
506
-
507
- unless SetFileAttributesW(wide_path, attributes)
508
- raise SystemCallError.new("SetFileAttributes", FFI.errno)
509
- end
510
-
511
- self
512
- end
513
-
514
- # Sets whether or not the file is being used for temporary storage.
515
- #
516
- # File systems avoid writing data back to mass storage if sufficient cache
517
- # memory is available, because often the application deletes the temporary
518
- # file shortly after the handle is closed. In that case, the system can
519
- # entirely avoid writing the data. Otherwise, the data will be written
520
- # after the handle is closed.
521
- #
522
- def temporary=(bool)
523
- wide_path = self.path.wincode
524
- attributes = GetFileAttributesW(wide_path)
525
-
526
- if attributes == INVALID_FILE_ATTRIBUTES
527
- raise SystemCallError.new("GetFileAttributes", FFI.errno)
528
- end
529
-
530
- if bool
531
- attributes |= FILE_ATTRIBUTE_TEMPORARY;
532
- else
533
- attributes &= ~FILE_ATTRIBUTE_TEMPORARY;
534
- end
535
-
536
- unless SetFileAttributesW(wide_path, attributes)
537
- raise SystemCallError.new("SetFileAttributes", FFI.errno)
538
- end
539
-
540
- self
541
- end
542
-
543
- private
544
-
545
- # Convenience method used internally for the various boolean singleton methods.
546
- #
547
- def self.check_for_attribute(file, attribute)
548
- attributes = GetFileAttributesW(file.wincode)
549
-
550
- if attributes == INVALID_FILE_ATTRIBUTES
551
- raise SystemCallError.new("GetFileAttributes", FFI.errno)
552
- end
553
-
554
- attributes & attribute > 0 ? true : false
555
- end
556
- end
1
+ require File.join(File.dirname(__FILE__), 'windows', 'constants')
2
+ require File.join(File.dirname(__FILE__), 'windows', 'structs')
3
+ require File.join(File.dirname(__FILE__), '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.2'
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
+ in_buf = FFI::MemoryPointer.new(:ulong)
280
+ bytes = FFI::MemoryPointer.new(:ulong)
281
+
282
+ compression_value = bool ? COMPRESSION_FORMAT_DEFAULT : COMPRESSION_FORMAT_NONE
283
+ in_buf.write_ulong(compression_value)
284
+
285
+ # We can't use get_osfhandle here because we need specific attributes
286
+ handle = CreateFileW(
287
+ self.path.wincode,
288
+ FILE_READ_DATA | FILE_WRITE_DATA,
289
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
290
+ nil,
291
+ OPEN_EXISTING,
292
+ 0,
293
+ 0
294
+ )
295
+
296
+ if handle == INVALID_HANDLE_VALUE
297
+ raise SystemCallError.new("CreateFile", FFI.errno)
298
+ end
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
+ end
318
+
319
+ self
320
+ end
321
+
322
+ # Sets the hidden attribute to true or false. Setting this attribute to
323
+ # true means that the file is not included in an ordinary directory listing.
324
+ #
325
+ def hidden=(bool)
326
+ wide_path = self.path.wincode
327
+ attributes = GetFileAttributesW(wide_path)
328
+
329
+ if attributes == INVALID_FILE_ATTRIBUTES
330
+ raise SystemCallError.new("GetFileAttributes", FFI.errno)
331
+ end
332
+
333
+ if bool
334
+ attributes |= FILE_ATTRIBUTE_HIDDEN;
335
+ else
336
+ attributes &= ~FILE_ATTRIBUTE_HIDDEN;
337
+ end
338
+
339
+ unless SetFileAttributesW(wide_path, attributes)
340
+ raise SystemCallError.new("SetFileAttributes", FFI.errno)
341
+ end
342
+
343
+ self
344
+ end
345
+
346
+ # Sets the 'indexed' attribute to true or false. Setting this to
347
+ # false means that the file will not be indexed by the content indexing
348
+ # service.
349
+ #
350
+ def indexed=(bool)
351
+ wide_path = self.path.wincode
352
+ attributes = GetFileAttributesW(wide_path)
353
+
354
+ if attributes == INVALID_FILE_ATTRIBUTES
355
+ raise SystemCallError.new("GetFileAttributes", FFI.errno)
356
+ end
357
+
358
+ if bool
359
+ attributes &= ~FILE_ATTRIBUTE_NOT_CONTENT_INDEXED;
360
+ else
361
+ attributes |= FILE_ATTRIBUTE_NOT_CONTENT_INDEXED;
362
+ end
363
+
364
+ unless SetFileAttributesW(wide_path, attributes)
365
+ raise SystemCallError.new("SetFileAttributes", FFI.errno)
366
+ end
367
+
368
+ self
369
+ end
370
+
371
+ alias :content_indexed= :indexed=
372
+
373
+ # Sets the normal attribute. Note that only 'true' is a valid argument,
374
+ # which has the effect of removing most other attributes. Attempting to
375
+ # pass any value except true will raise an ArgumentError.
376
+ #
377
+ def normal=(bool)
378
+ unless bool
379
+ raise ArgumentError, "only 'true' may be passed as an argument"
380
+ end
381
+
382
+ unless SetFileAttributesW(self.path.wincode, FILE_ATTRIBUTE_NORMAL)
383
+ raise SystemCallError.new("SetFileAttributes", FFI.errno)
384
+ end
385
+
386
+ self
387
+ end
388
+
389
+ # Sets whether or not a file is online or not. Setting this to false means
390
+ # that the data of the file is not immediately available. This attribute
391
+ # indicates that the file data has been physically moved to offline storage.
392
+ # This attribute is used by Remote Storage, the hierarchical storage
393
+ # management software.
394
+ #
395
+ # Applications should not arbitrarily change this attribute.
396
+ #
397
+ def offline=(bool)
398
+ wide_path = self.path.wincode
399
+ attributes = GetFileAttributesW(wide_path)
400
+
401
+ if attributes == INVALID_FILE_ATTRIBUTES
402
+ raise SystemCallError.new("GetFileAttributes", FFI.errno)
403
+ end
404
+
405
+ if bool
406
+ attributes |= FILE_ATTRIBUTE_OFFLINE;
407
+ else
408
+ attributes &= ~FILE_ATTRIBUTE_OFFLINE;
409
+ end
410
+
411
+ unless SetFileAttributesW(wide_path, attributes)
412
+ raise SystemCallError.new("SetFileAttributes", FFI.errno)
413
+ end
414
+
415
+ self
416
+ end
417
+
418
+ # Sets the readonly attribute. If set to true the the file or directory is
419
+ # readonly. Applications can read the file but cannot write to it or delete
420
+ # it. In the case of a directory, applications cannot delete it.
421
+ #
422
+ def readonly=(bool)
423
+ wide_path = self.path.wincode
424
+ attributes = GetFileAttributesW(wide_path)
425
+
426
+ if attributes == INVALID_FILE_ATTRIBUTES
427
+ raise SystemCallError.new("GetFileAttributes", FFI.errno)
428
+ end
429
+
430
+ if bool
431
+ attributes |= FILE_ATTRIBUTE_READONLY;
432
+ else
433
+ attributes &= ~FILE_ATTRIBUTE_READONLY;
434
+ end
435
+
436
+ unless SetFileAttributesW(wide_path, attributes)
437
+ raise SystemCallError.new("SetFileAttributes", FFI.errno)
438
+ end
439
+
440
+ self
441
+ end
442
+
443
+ # Sets the file to a sparse (usually image) file. Note that you cannot
444
+ # remove the sparse property from a file.
445
+ #
446
+ def sparse=(bool)
447
+ unless bool
448
+ warn 'cannot remove sparse property from a file - operation ignored'
449
+ return
450
+ end
451
+
452
+ bytes = FFI::MemoryPointer.new(:ulong)
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
+ begin
469
+ bool = DeviceIoControl(
470
+ handle,
471
+ FSCTL_SET_SPARSE(),
472
+ nil,
473
+ 0,
474
+ nil,
475
+ 0,
476
+ bytes,
477
+ nil
478
+ )
479
+
480
+ unless bool == 0
481
+ raise SystemCallError.new("DeviceIoControl", FFI.errno)
482
+ end
483
+ ensure
484
+ CloseHandle(handle)
485
+ end
486
+
487
+ self
488
+ end
489
+
490
+ # Set whether or not the file is a system file. A system file is a file
491
+ # that is part of the operating system or is used exclusively by it.
492
+ #
493
+ def system=(bool)
494
+ wide_path = self.path.wincode
495
+ attributes = GetFileAttributesW(wide_path)
496
+
497
+ if attributes == INVALID_FILE_ATTRIBUTES
498
+ raise SystemCallError.new("GetFileAttributes", FFI.errno)
499
+ end
500
+
501
+ if bool
502
+ attributes |= FILE_ATTRIBUTE_SYSTEM;
503
+ else
504
+ attributes &= ~FILE_ATTRIBUTE_SYSTEM;
505
+ end
506
+
507
+ unless SetFileAttributesW(wide_path, attributes)
508
+ raise SystemCallError.new("SetFileAttributes", FFI.errno)
509
+ end
510
+
511
+ self
512
+ end
513
+
514
+ # Sets whether or not the file is being used for temporary storage.
515
+ #
516
+ # File systems avoid writing data back to mass storage if sufficient cache
517
+ # memory is available, because often the application deletes the temporary
518
+ # file shortly after the handle is closed. In that case, the system can
519
+ # entirely avoid writing the data. Otherwise, the data will be written
520
+ # after the handle is closed.
521
+ #
522
+ def temporary=(bool)
523
+ wide_path = self.path.wincode
524
+ attributes = GetFileAttributesW(wide_path)
525
+
526
+ if attributes == INVALID_FILE_ATTRIBUTES
527
+ raise SystemCallError.new("GetFileAttributes", FFI.errno)
528
+ end
529
+
530
+ if bool
531
+ attributes |= FILE_ATTRIBUTE_TEMPORARY;
532
+ else
533
+ attributes &= ~FILE_ATTRIBUTE_TEMPORARY;
534
+ end
535
+
536
+ unless SetFileAttributesW(wide_path, attributes)
537
+ raise SystemCallError.new("SetFileAttributes", FFI.errno)
538
+ end
539
+
540
+ self
541
+ end
542
+
543
+ private
544
+
545
+ # Used to simulate Ruby's allowance for objects that implement to_str or to_path.
546
+ #
547
+ def self.string_check(arg)
548
+ return arg if arg.is_a?(String)
549
+ return arg.send(:to_str) if arg.respond_to?(:to_str, true)
550
+ return arg.to_path if arg.respond_to?(:to_path)
551
+ raise TypeError
552
+ end
553
+
554
+ # Convenience method used internally for the various boolean singleton methods.
555
+ #
556
+ def self.check_for_attribute(file, attribute)
557
+ file = string_check(file)
558
+ attributes = GetFileAttributesW(file.wincode)
559
+
560
+ if attributes == INVALID_FILE_ATTRIBUTES
561
+ raise SystemCallError.new("GetFileAttributes", FFI.errno)
562
+ end
563
+
564
+ attributes & attribute > 0 ? true : false
565
+ end
566
+ end