win32-file-attributes 1.0.1 → 1.0.2

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