win32-changejournal 0.3.2 → 0.3.3
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +8 -0
- data/MANIFEST +10 -11
- data/README +9 -28
- data/Rakefile +96 -0
- data/examples/example_changejournal.rb +23 -0
- data/ext/extconf.rb +36 -0
- data/ext/win32/changejournal.c +57 -23
- data/ext/win32/changejournal.h +356 -0
- data/test/{tc_changejournal.rb → test_win32_changejournal.rb} +27 -12
- data/win32-changejournal.gemspec +36 -0
- metadata +31 -16
- data/lib/win32/changejournal.so +0 -0
data/CHANGES
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
== 0.3.3 - 18-Aug-2009
|
2
|
+
* Changed license to Artistic 2.0.
|
3
|
+
* ChangeJournalStruct objects are now frozen. This is read-only data.
|
4
|
+
* ChangeJournalError is now ChangeJournal::Error.
|
5
|
+
* Added test-unit 2.x as a development dependency.
|
6
|
+
* Renamed the test and example files.
|
7
|
+
* Some updates to the Rakefile and gemspec.
|
8
|
+
|
1
9
|
== 0.3.2 - 27-Apr-2008
|
2
10
|
* Fixed RubyForge bug #10555 (bignum too big to convert into long).
|
3
11
|
* Lots of internal reorganization, partially in preparation for the
|
data/MANIFEST
CHANGED
@@ -1,11 +1,10 @@
|
|
1
|
-
CHANGES
|
2
|
-
README
|
3
|
-
MANIFEST
|
4
|
-
Rakefile
|
5
|
-
win32-changejournal.gemspec
|
6
|
-
ext/extconf.rb
|
7
|
-
ext/win32/changejournal.c
|
8
|
-
ext/win32/changejournal.h
|
9
|
-
examples/
|
10
|
-
|
11
|
-
test/tc_changejournal.rb
|
1
|
+
* CHANGES
|
2
|
+
* README
|
3
|
+
* MANIFEST
|
4
|
+
* Rakefile
|
5
|
+
* win32-changejournal.gemspec
|
6
|
+
* ext/extconf.rb
|
7
|
+
* ext/win32/changejournal.c
|
8
|
+
* ext/win32/changejournal.h
|
9
|
+
* examples/example_changejournal.rb
|
10
|
+
* test/test_win32_changejournal.rb
|
data/README
CHANGED
@@ -2,8 +2,10 @@
|
|
2
2
|
A class for monitoring events related to files and directories on NTFS.
|
3
3
|
|
4
4
|
== Installation
|
5
|
-
|
6
|
-
|
5
|
+
=== Gem Installation
|
6
|
+
gem install win32-changejournal
|
7
|
+
=== Local Installation
|
8
|
+
rake install
|
7
9
|
|
8
10
|
== Synopsis
|
9
11
|
require 'win32/changejournal'
|
@@ -11,8 +13,7 @@
|
|
11
13
|
|
12
14
|
# Indefinitely wait for a change in 'C:\' and any of its
|
13
15
|
# subdirectories. Print the file and action affected.
|
14
|
-
|
15
|
-
cj = ChangeJournal.new('C:\')
|
16
|
+
cj = ChangeJournal.new("C:\\")
|
16
17
|
|
17
18
|
cj.wait{ |array|
|
18
19
|
array.each{ |info|
|
@@ -23,34 +24,14 @@
|
|
23
24
|
}
|
24
25
|
|
25
26
|
c.delete
|
26
|
-
|
27
|
-
== Class Methods
|
28
|
-
ChangeJournal.new(drive)
|
29
|
-
Returns a new ChangeJournal object and places a monitor on +drive+.
|
30
|
-
|
31
|
-
== Instance Methods
|
32
|
-
ChangeJournal#wait(num_seconds=INFINITE)
|
33
|
-
ChangeJournal#wait(num_seconds=INFINITE){ |arr| ... }
|
34
|
-
|
35
|
-
Waits up to 'num_seconds' for a notification to occur, or infinitely if
|
36
|
-
no value is specified.
|
37
|
-
|
38
|
-
If a block is provided, yields an array of ChangeJournalStruct's that
|
39
|
-
contains three members: file_name, action, and path. An array is returned
|
40
|
-
because multiple actions can be associated with a single event.
|
41
|
-
|
42
|
-
== Constants
|
43
|
-
=== Standard constants
|
44
|
-
VERSION
|
45
|
-
Returns the current version number of this library as a String.
|
46
27
|
|
47
28
|
== Notes
|
48
29
|
Based on what the MSDN documentation says, this library requires NTFS, and
|
49
|
-
should be preferred on that filesystem.
|
30
|
+
should be preferred on that filesystem. On FAT filesystems, you should
|
50
31
|
use the win32-changenotify library instead.
|
51
32
|
|
52
33
|
== Acknowledgements
|
53
|
-
This
|
34
|
+
This library was originally based on the CJTest module by Jeffrey
|
54
35
|
Cooperstein & Jeffrey Richter.
|
55
36
|
|
56
37
|
== Future Plans
|
@@ -61,10 +42,10 @@ VERSION
|
|
61
42
|
project page at http://www.rubyforge.net/projects/win32utils
|
62
43
|
|
63
44
|
== License
|
64
|
-
|
45
|
+
Artistic 2.0
|
65
46
|
|
66
47
|
== Copyright
|
67
|
-
(C) 2003-
|
48
|
+
(C) 2003-2009 Daniel J. Berger, All Rights Reserved
|
68
49
|
|
69
50
|
== Warranty
|
70
51
|
This library is provided "as is" and without any express or
|
data/Rakefile
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/clean'
|
3
|
+
require 'rake/testtask'
|
4
|
+
require 'rbconfig'
|
5
|
+
include Config
|
6
|
+
|
7
|
+
desc 'Install the win32-changejournal library'
|
8
|
+
task :install => [:build] do
|
9
|
+
Dir.chdir('ext'){
|
10
|
+
sh 'nmake install'
|
11
|
+
}
|
12
|
+
end
|
13
|
+
|
14
|
+
desc 'Clean any build files for win32-changejournal'
|
15
|
+
task :clean do
|
16
|
+
Dir.chdir('ext') do
|
17
|
+
sh 'nmake distclean' if File.exists?('changejournal.obj')
|
18
|
+
rm 'win32/changejournal.so' if File.exists?('win32/changejournal.so')
|
19
|
+
end
|
20
|
+
rm_rf('lib') if File.exists?('lib')
|
21
|
+
end
|
22
|
+
|
23
|
+
desc "Build win32-changejournal (but don't install it)"
|
24
|
+
task :build => [:clean] do
|
25
|
+
Dir.chdir('ext') do
|
26
|
+
ruby 'extconf.rb'
|
27
|
+
sh 'nmake'
|
28
|
+
mv 'changejournal.so', 'win32' # For the test suite
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
desc 'Build gem'
|
33
|
+
task :build_gem do
|
34
|
+
eval(IO.read('win32-changejournal.gemspec'))
|
35
|
+
end
|
36
|
+
|
37
|
+
desc 'Build a binary gem'
|
38
|
+
task :build_binary_gem => [:build] do
|
39
|
+
mkdir_p 'lib/win32'
|
40
|
+
mv 'ext/win32/changejournal.so', 'lib/win32'
|
41
|
+
task :build_binary_gem => [:clean]
|
42
|
+
|
43
|
+
spec = Gem::Specification.new do |gem|
|
44
|
+
gem.name = 'win32-changejournal'
|
45
|
+
gem.version = '0.3.3'
|
46
|
+
gem.authors = ['Daniel J. Berger', 'Park Heesob']
|
47
|
+
gem.license = 'Artistic 2.0'
|
48
|
+
gem.email = 'djberg96@gmail.com'
|
49
|
+
gem.homepage = 'http://www.rubyforge.org/projects/win32utils'
|
50
|
+
gem.platform = Gem::Platform::CURRENT
|
51
|
+
gem.summary = 'A library for monitoring files and directories on NTFS'
|
52
|
+
gem.has_rdoc = true
|
53
|
+
gem.test_file = 'test/test_win32_changejournal.rb'
|
54
|
+
|
55
|
+
gem.files = [
|
56
|
+
Dir['*'],
|
57
|
+
Dir['test/*'],
|
58
|
+
Dir['examples/*'],
|
59
|
+
'ext/extconf.rb',
|
60
|
+
'ext/win32/changejournal.c',
|
61
|
+
'lib/win32/changejournal.so'
|
62
|
+
].flatten.reject{ |f| f.include?('CVS') }
|
63
|
+
|
64
|
+
gem.required_ruby_version = '>= 1.8.2'
|
65
|
+
gem.rubyforge_project = 'win32utils'
|
66
|
+
|
67
|
+
gem.extra_rdoc_files = [
|
68
|
+
'README',
|
69
|
+
'CHANGES',
|
70
|
+
'MANIFEST',
|
71
|
+
'ext/win32/changejournal.c'
|
72
|
+
]
|
73
|
+
|
74
|
+
gem.add_development_dependency('test-unit', '>= 2.0.3')
|
75
|
+
|
76
|
+
gem.description = <<-EOF
|
77
|
+
The win32-changejournal library provides an interface for MS Windows
|
78
|
+
change journals. A change journal is a record of any changes on a given
|
79
|
+
volume maintained by the operating system itself.
|
80
|
+
EOF
|
81
|
+
end
|
82
|
+
|
83
|
+
Gem::Builder.new(spec).build
|
84
|
+
end
|
85
|
+
|
86
|
+
desc 'Run the example program'
|
87
|
+
task :example => [:build] do |t|
|
88
|
+
ruby '-Iext examples/example_changejournal.rb'
|
89
|
+
end
|
90
|
+
|
91
|
+
Rake::TestTask.new(:test) do |t|
|
92
|
+
task :test => [:build]
|
93
|
+
t.libs << 'ext'
|
94
|
+
t.warning = true
|
95
|
+
t.verbose = true
|
96
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
########################################################################
|
2
|
+
# example_changejournal.rb
|
3
|
+
#
|
4
|
+
# A test script for general futzing. Modify as you see fit. You can
|
5
|
+
# run this test script via the 'rake example' task.
|
6
|
+
########################################################################
|
7
|
+
require 'win32/changejournal'
|
8
|
+
|
9
|
+
puts 'VERSION: ' + Win32::ChangeJournal::VERSION
|
10
|
+
|
11
|
+
cj = Win32::ChangeJournal.new('c:\\')
|
12
|
+
|
13
|
+
# Wait up to 5 minutes for a change journals
|
14
|
+
cj.wait(300){ |array|
|
15
|
+
array.each{ |struct|
|
16
|
+
puts 'Something changed'
|
17
|
+
puts 'File: ' + struct.file_name
|
18
|
+
puts 'Action: ' + struct.action
|
19
|
+
puts 'Path: ' + struct.path
|
20
|
+
}
|
21
|
+
}
|
22
|
+
|
23
|
+
cj.delete
|
data/ext/extconf.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'mkmf'
|
2
|
+
require 'Win32API'
|
3
|
+
|
4
|
+
# Ensure that win32-ipc is installed.
|
5
|
+
begin
|
6
|
+
require 'win32/ipc'
|
7
|
+
rescue LoadError
|
8
|
+
STDERR.puts 'The win32-ipc library is a prerequisite for this package.'
|
9
|
+
STDERR.puts 'Please install win32-ipc before continuing.'
|
10
|
+
STDERR.puts 'Exiting...'
|
11
|
+
exit
|
12
|
+
end
|
13
|
+
|
14
|
+
# Set $CPPFLAGS as needed since mkmf doesn't do this for us.
|
15
|
+
GetVersionEx = Win32API.new('kernel32','GetVersionEx','P','I')
|
16
|
+
swCSDVersion = "\0" * 128
|
17
|
+
OSVERSIONINFO = [148,0,0,0,0,swCSDVersion].pack("LLLLLa128")
|
18
|
+
GetVersionEx.call(OSVERSIONINFO)
|
19
|
+
info = OSVERSIONINFO.unpack("LLLLLa128")
|
20
|
+
|
21
|
+
major, minor = info[1,2]
|
22
|
+
platform = info[4]
|
23
|
+
macro = '_WIN32_WINNT='
|
24
|
+
|
25
|
+
case minor
|
26
|
+
when 2 # 2003
|
27
|
+
macro += '0x0502'
|
28
|
+
when 1 # XP
|
29
|
+
macro += '0x0501'
|
30
|
+
else # 2000
|
31
|
+
macro += '0x0500'
|
32
|
+
end
|
33
|
+
|
34
|
+
$CPPFLAGS += " -D#{macro}"
|
35
|
+
|
36
|
+
create_makefile('win32/changejournal', 'win32')
|
data/ext/win32/changejournal.c
CHANGED
@@ -155,7 +155,7 @@ PUSN_RECORD EnumNext(ChangeJournalStruct *ptr) {
|
|
155
155
|
|
156
156
|
// Make sure we have a buffer to use
|
157
157
|
if(ptr->pbCJData == NULL)
|
158
|
-
rb_raise(
|
158
|
+
rb_raise(cCJError, "make sure we have a buffer to use");
|
159
159
|
|
160
160
|
// If we do not have a record loaded, or enumerating to the next record
|
161
161
|
// will point us past the end of the output buffer returned
|
@@ -233,7 +233,7 @@ void CleanUp(ChangeJournalStruct *ptr) {
|
|
233
233
|
*/
|
234
234
|
BOOL Init(ChangeJournalStruct *ptr, TCHAR cDriveLetter, DWORD cbBuffer){
|
235
235
|
if(ptr->pbCJData != NULL){
|
236
|
-
rb_raise(
|
236
|
+
rb_raise(cCJError,
|
237
237
|
"you should not call this function twice for one instance."
|
238
238
|
);
|
239
239
|
}
|
@@ -295,7 +295,7 @@ void InitialzeForMonitoring(ChangeJournalStruct *ptr) {
|
|
295
295
|
default:
|
296
296
|
// Some other error happened while querying the journal information.
|
297
297
|
// There is nothing we can do from here
|
298
|
-
rb_raise(
|
298
|
+
rb_raise(cCJError, "unable to query journal");
|
299
299
|
fOk = FALSE;
|
300
300
|
break;
|
301
301
|
}
|
@@ -324,9 +324,14 @@ static VALUE changejournal_allocate(VALUE klass){
|
|
324
324
|
/*
|
325
325
|
* :call-seq:
|
326
326
|
*
|
327
|
-
* ChangeJournal.new(
|
327
|
+
* Win32::ChangeJournal.new(volume)
|
328
328
|
*
|
329
|
-
* Returns a new ChangeJournal object and places a monitor on
|
329
|
+
* Returns a new ChangeJournal object and places a monitor on +volume+.
|
330
|
+
*
|
331
|
+
* Example:
|
332
|
+
*
|
333
|
+
* # Put a change journal monitor on the C: drive
|
334
|
+
* cj = Win32::Change::Journal.new("C:\\")
|
330
335
|
*/
|
331
336
|
static VALUE changejournal_init(VALUE self, VALUE v_drive)
|
332
337
|
{
|
@@ -337,7 +342,7 @@ static VALUE changejournal_init(VALUE self, VALUE v_drive)
|
|
337
342
|
|
338
343
|
// Do not allow a block for this class
|
339
344
|
if(rb_block_given_p())
|
340
|
-
rb_raise(
|
345
|
+
rb_raise(cCJError, "block not permitted for this class");
|
341
346
|
|
342
347
|
// Initialize member variables
|
343
348
|
ptr->hCJ = INVALID_HANDLE_VALUE;
|
@@ -346,8 +351,9 @@ static VALUE changejournal_init(VALUE self, VALUE v_drive)
|
|
346
351
|
ptr->pbCJData = NULL;
|
347
352
|
ptr->pUsnRecord = NULL;
|
348
353
|
|
349
|
-
|
350
|
-
|
354
|
+
/* Initialize the ChangeJournal object with the current drive letter and tell
|
355
|
+
* it to allocate a buffer of 10000 bytes to read journal records.
|
356
|
+
*/
|
351
357
|
if(!Init(ptr, lpDriveLetter[0], 10000))
|
352
358
|
rb_raise(rb_eTypeError, "initialization error");
|
353
359
|
|
@@ -359,14 +365,28 @@ static VALUE changejournal_init(VALUE self, VALUE v_drive)
|
|
359
365
|
/*
|
360
366
|
* :call-seq:
|
361
367
|
*
|
362
|
-
* ChangeJournal#wait(num_seconds=INFINITE)
|
363
|
-
* ChangeJournal#wait(num_seconds=INFINITE){ |
|
368
|
+
* Win32::ChangeJournal#wait(num_seconds=INFINITE)
|
369
|
+
* Win32::ChangeJournal#wait(num_seconds=INFINITE){ |struct| ... }
|
364
370
|
*
|
365
371
|
* Waits up to 'num_seconds' for a notification to occur, or infinitely if
|
366
372
|
* no value is specified.
|
367
373
|
*
|
368
|
-
* If a block is provided, yields a ChangeJournalStruct that contains
|
369
|
-
* members - file_name and
|
374
|
+
* If a block is provided, yields a frozen ChangeJournalStruct that contains
|
375
|
+
* three members - file_name, action, and path.
|
376
|
+
*
|
377
|
+
* Example:
|
378
|
+
*
|
379
|
+
* require 'win32/changejournal'
|
380
|
+
* cj = Win32::ChangeJournal.new("C:\\")
|
381
|
+
*
|
382
|
+
* # Wait for something to happen for 5 seconds, but ignore the details
|
383
|
+
* cj.wait(5)
|
384
|
+
*
|
385
|
+
* # Wait for 5 seconds, print the details if any, then bail out
|
386
|
+
* cj.wait(5){ |struct| p struct }
|
387
|
+
*
|
388
|
+
* # Wait indefinitely, and print the details when something happens
|
389
|
+
* cj.wait{ |struct| p struct }
|
370
390
|
*/
|
371
391
|
static VALUE changejournal_wait(int argc, VALUE* argv, VALUE self){
|
372
392
|
VALUE v_timeout, v_block;
|
@@ -411,7 +431,7 @@ static VALUE changejournal_wait(int argc, VALUE* argv, VALUE self){
|
|
411
431
|
|
412
432
|
switch(dwWait){
|
413
433
|
case WAIT_FAILED:
|
414
|
-
rb_raise(
|
434
|
+
rb_raise(cCJError, ErrorDescription(GetLastError()));
|
415
435
|
break;
|
416
436
|
case WAIT_OBJECT_0:
|
417
437
|
rb_iv_set(self, "@signaled", Qtrue);
|
@@ -427,7 +447,7 @@ static VALUE changejournal_wait(int argc, VALUE* argv, VALUE self){
|
|
427
447
|
return INT2NUM(0);
|
428
448
|
break;
|
429
449
|
default:
|
430
|
-
rb_raise(
|
450
|
+
rb_raise(cCJError,
|
431
451
|
"unknown return value from WaitForSingleObject()"
|
432
452
|
);
|
433
453
|
};
|
@@ -438,10 +458,17 @@ static VALUE changejournal_wait(int argc, VALUE* argv, VALUE self){
|
|
438
458
|
/*
|
439
459
|
* call-seq:
|
440
460
|
*
|
441
|
-
* ChangeJournal#delete
|
461
|
+
* Win32::ChangeJournal#delete
|
442
462
|
*
|
443
463
|
* Deletes the change journal on a volume, or waits for notification of
|
444
464
|
* change journal deletion.
|
465
|
+
*
|
466
|
+
* Example:
|
467
|
+
*
|
468
|
+
* require 'win32/changejournal'
|
469
|
+
* cj = Win32::ChangeJournal.new("C:\\")
|
470
|
+
* cj.wait(5)
|
471
|
+
* cj.delete
|
445
472
|
*/
|
446
473
|
static VALUE changejournal_delete(VALUE self){
|
447
474
|
ChangeJournalStruct *ptr;
|
@@ -459,19 +486,27 @@ void Init_changejournal()
|
|
459
486
|
{
|
460
487
|
VALUE mWin32, cChangeJournal;
|
461
488
|
|
462
|
-
|
489
|
+
/* The Win32 module serves as a namespace only. */
|
463
490
|
mWin32 = rb_define_module("Win32");
|
491
|
+
|
492
|
+
/* The Win32::ChangeJournal class encapsulates functions related to a
|
493
|
+
* Windows change journal.
|
494
|
+
*/
|
464
495
|
cChangeJournal = rb_define_class_under(mWin32, "ChangeJournal", rb_cObject);
|
465
|
-
cChangeJournalError = rb_define_class_under(mWin32, "ChangeJournalError",
|
466
|
-
rb_eStandardError);
|
467
496
|
|
468
|
-
|
469
|
-
|
497
|
+
/* The ChangeJournal::Error class is typically raised if any of the
|
498
|
+
* Win32::ChangeJournal methods fail.
|
499
|
+
*/
|
500
|
+
cCJError = rb_define_class_under(cChangeJournal, "Error", rb_eStandardError);
|
501
|
+
|
502
|
+
/* ChangeJournal class and instance methods */
|
503
|
+
rb_define_alloc_func(cChangeJournal, changejournal_allocate);
|
504
|
+
|
470
505
|
rb_define_method(cChangeJournal, "initialize", changejournal_init, 1);
|
471
506
|
rb_define_method(cChangeJournal, "wait", changejournal_wait, -1);
|
472
507
|
rb_define_method(cChangeJournal, "delete", changejournal_delete, 0);
|
473
508
|
|
474
|
-
|
509
|
+
/* Struct definitions */
|
475
510
|
sChangeJournalStruct = rb_struct_define(
|
476
511
|
"ChangeJournalStruct",
|
477
512
|
"action",
|
@@ -480,8 +515,7 @@ void Init_changejournal()
|
|
480
515
|
0
|
481
516
|
);
|
482
517
|
|
483
|
-
|
518
|
+
/* 0.3.3: The version of the win32-changejournal library */
|
484
519
|
rb_define_const(cChangeJournal, "VERSION",
|
485
520
|
rb_str_new2(WIN32_CHANGEJOURNAL_VERSION));
|
486
521
|
}
|
487
|
-
|
@@ -0,0 +1,356 @@
|
|
1
|
+
#define WIN32_CHANGEJOURNAL_VERSION "0.3.3"
|
2
|
+
#define MAX_STRING 1024
|
3
|
+
|
4
|
+
static VALUE cCJError;
|
5
|
+
static VALUE sChangeJournalStruct;
|
6
|
+
static char error[MAX_STRING];
|
7
|
+
static VALUE hPathDB;
|
8
|
+
|
9
|
+
// Prototype
|
10
|
+
BOOL Query(ChangeJournalStruct, PUSN_JOURNAL_DATA);
|
11
|
+
void add_reason(char*, const char*);
|
12
|
+
|
13
|
+
struct changejournalstruct {
|
14
|
+
TCHAR cDriveLetter; // drive letter of volume
|
15
|
+
HANDLE hCJ; // handle to volume
|
16
|
+
|
17
|
+
// Members used to enumerate journal records
|
18
|
+
READ_USN_JOURNAL_DATA rujd; // parameters for reading records
|
19
|
+
PBYTE pbCJData; // buffer of records
|
20
|
+
DWORD cbCJData; // valid bytes in buffer
|
21
|
+
PUSN_RECORD pUsnRecord; // pointer to current record
|
22
|
+
|
23
|
+
// Members used to notify application of new data
|
24
|
+
HANDLE hCJAsync; // Async handle to volume
|
25
|
+
OVERLAPPED oCJAsync; // overlapped structure
|
26
|
+
USN UsnAsync; // output buffer for overlapped I/O
|
27
|
+
DWORD dwDelay; // delay before sending notification
|
28
|
+
};
|
29
|
+
|
30
|
+
typedef struct changejournalstruct ChangeJournalStruct;
|
31
|
+
|
32
|
+
void CleanUp(ChangeJournalStruct *ptr);
|
33
|
+
|
34
|
+
static void changejournal_free(ChangeJournalStruct *p)
|
35
|
+
{
|
36
|
+
CleanUp(p);
|
37
|
+
free(p);
|
38
|
+
}
|
39
|
+
|
40
|
+
// Helper function for the get_file_action function.
|
41
|
+
void add_reason(char* str, const char* reason){
|
42
|
+
if(strlen(str) > 0)
|
43
|
+
strcat(str, ", ");
|
44
|
+
|
45
|
+
strcat(str, reason);
|
46
|
+
}
|
47
|
+
|
48
|
+
PUSN_RECORD EnumNext(ChangeJournalStruct *ptr);
|
49
|
+
|
50
|
+
// Enumerate the MFT for all entries. Store the file reference numbers of
|
51
|
+
// any directories in the database.
|
52
|
+
void PopulatePath(ChangeJournalStruct *ptr) {
|
53
|
+
USN_JOURNAL_DATA ujd;
|
54
|
+
BY_HANDLE_FILE_INFORMATION fi;
|
55
|
+
TCHAR szRoot[_MAX_PATH];
|
56
|
+
HANDLE hDir;
|
57
|
+
MFT_ENUM_DATA med;
|
58
|
+
BYTE pData[sizeof(DWORDLONG) + 0x10000]; // Process MFT in 64k chunks
|
59
|
+
DWORDLONG fnLast = 0;
|
60
|
+
DWORDLONG IndexRoot, FRN, PFRN;
|
61
|
+
DWORD cb;
|
62
|
+
LPWSTR pszFileName;
|
63
|
+
WCHAR szFile[MAX_PATH];
|
64
|
+
int cFileName;
|
65
|
+
VALUE v_path_db;
|
66
|
+
|
67
|
+
v_path_db = rb_hash_new();
|
68
|
+
Query(ptr, &ujd);
|
69
|
+
|
70
|
+
// Get the FRN of the root directory
|
71
|
+
wsprintf(szRoot, TEXT("%c:\\"), ptr->cDriveLetter);
|
72
|
+
|
73
|
+
hDir = CreateFile(
|
74
|
+
szRoot,
|
75
|
+
0,
|
76
|
+
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
77
|
+
NULL,
|
78
|
+
OPEN_EXISTING,
|
79
|
+
FILE_FLAG_BACKUP_SEMANTICS,
|
80
|
+
NULL
|
81
|
+
);
|
82
|
+
|
83
|
+
GetFileInformationByHandle(hDir, &fi);
|
84
|
+
CloseHandle(hDir);
|
85
|
+
|
86
|
+
IndexRoot = (((DWORDLONG) fi.nFileIndexHigh) << 32) | fi.nFileIndexLow;
|
87
|
+
|
88
|
+
wsprintf(szRoot, TEXT("%c:"), ptr->cDriveLetter);
|
89
|
+
|
90
|
+
FRN = IndexRoot;
|
91
|
+
PFRN = 0;
|
92
|
+
|
93
|
+
rb_hash_aset(v_path_db,ULL2NUM(FRN),
|
94
|
+
rb_ary_new3(2, rb_str_new(szRoot,2), ULL2NUM(PFRN)));
|
95
|
+
|
96
|
+
med.StartFileReferenceNumber = 0;
|
97
|
+
med.LowUsn = 0;
|
98
|
+
med.HighUsn = ujd.NextUsn;
|
99
|
+
|
100
|
+
while (DeviceIoControl(ptr->hCJ, FSCTL_ENUM_USN_DATA, &med, sizeof(med),
|
101
|
+
pData, sizeof(pData), &cb, NULL) != FALSE) {
|
102
|
+
PUSN_RECORD pRecord = (PUSN_RECORD) &pData[sizeof(USN)];
|
103
|
+
|
104
|
+
while((PBYTE) pRecord < (pData + cb)){
|
105
|
+
if(0 != (pRecord->FileAttributes & FILE_ATTRIBUTE_DIRECTORY)){
|
106
|
+
pszFileName = (LPWSTR)((PBYTE) pRecord + pRecord->FileNameOffset);
|
107
|
+
cFileName = pRecord->FileNameLength / sizeof(WCHAR);
|
108
|
+
|
109
|
+
wcsncpy(szFile, pszFileName, cFileName);
|
110
|
+
szFile[cFileName] = 0;
|
111
|
+
|
112
|
+
FRN = pRecord->FileReferenceNumber;
|
113
|
+
PFRN = pRecord->ParentFileReferenceNumber;
|
114
|
+
|
115
|
+
WideCharToMultiByte(
|
116
|
+
CP_ACP,
|
117
|
+
0,
|
118
|
+
szFile,
|
119
|
+
-1,
|
120
|
+
szRoot,
|
121
|
+
MAX_PATH,
|
122
|
+
NULL,
|
123
|
+
NULL
|
124
|
+
);
|
125
|
+
|
126
|
+
rb_hash_aset(
|
127
|
+
v_path_db,
|
128
|
+
ULL2NUM(FRN),
|
129
|
+
rb_ary_new3(2, rb_str_new2((char*)szRoot), ULL2NUM(PFRN))
|
130
|
+
);
|
131
|
+
|
132
|
+
}
|
133
|
+
pRecord = (PUSN_RECORD) ((PBYTE) pRecord + pRecord->RecordLength);
|
134
|
+
}
|
135
|
+
|
136
|
+
med.StartFileReferenceNumber = * (DWORDLONG *) pData;
|
137
|
+
}
|
138
|
+
|
139
|
+
hPathDB = v_path_db;
|
140
|
+
}
|
141
|
+
|
142
|
+
// Returns both the file action and name in a Ruby struct
|
143
|
+
static VALUE get_file_action(ChangeJournalStruct *ptr){
|
144
|
+
VALUE v_struct, v_action, v_array, v_arr, v_path, v_path_array;
|
145
|
+
WCHAR szFile[MAX_PATH];
|
146
|
+
char file_name[MAX_PATH];
|
147
|
+
char path[MAX_PATH];
|
148
|
+
char szReason[MAX_STRING];
|
149
|
+
LPWSTR pszFileName;
|
150
|
+
int cFileName;
|
151
|
+
DWORDLONG FRN;
|
152
|
+
DWORDLONG PFRN;
|
153
|
+
VALUE v_path_db = hPathDB;
|
154
|
+
|
155
|
+
v_array = rb_ary_new();
|
156
|
+
|
157
|
+
while(EnumNext(ptr)) {
|
158
|
+
pszFileName =
|
159
|
+
(LPWSTR)((PBYTE) ptr->pUsnRecord + ptr->pUsnRecord->FileNameOffset);
|
160
|
+
|
161
|
+
cFileName = ptr->pUsnRecord->FileNameLength / sizeof(WCHAR);
|
162
|
+
wcsncpy(szFile, pszFileName, cFileName);
|
163
|
+
szFile[cFileName] = 0;
|
164
|
+
|
165
|
+
// If this is a close record for a directory, we may need to adjust
|
166
|
+
// our directory database
|
167
|
+
|
168
|
+
if(0 != (ptr->pUsnRecord->FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
169
|
+
&& 0 != (ptr->pUsnRecord->Reason & USN_REASON_CLOSE))
|
170
|
+
{
|
171
|
+
|
172
|
+
FRN = ptr->pUsnRecord->FileReferenceNumber;
|
173
|
+
PFRN = ptr->pUsnRecord->ParentFileReferenceNumber;
|
174
|
+
|
175
|
+
WideCharToMultiByte(
|
176
|
+
CP_ACP,
|
177
|
+
0,
|
178
|
+
szFile,
|
179
|
+
-1,
|
180
|
+
path,
|
181
|
+
MAX_PATH,
|
182
|
+
NULL,
|
183
|
+
NULL
|
184
|
+
);
|
185
|
+
|
186
|
+
// Process newly created directories
|
187
|
+
if(0 != (ptr->pUsnRecord->Reason & USN_REASON_FILE_CREATE)){
|
188
|
+
rb_hash_aset(v_path_db,ULL2NUM(FRN),
|
189
|
+
rb_ary_new3(2, rb_str_new2(path), ULL2NUM(PFRN)));
|
190
|
+
}
|
191
|
+
|
192
|
+
// Process renamed directories
|
193
|
+
if(0 != (ptr->pUsnRecord->Reason & USN_REASON_RENAME_NEW_NAME)) {
|
194
|
+
rb_hash_aset(v_path_db,ULL2NUM(FRN),
|
195
|
+
rb_ary_new3(2, rb_str_new2(path), ULL2NUM(PFRN)));
|
196
|
+
}
|
197
|
+
|
198
|
+
// Process deleted directories
|
199
|
+
if(0 != (ptr->pUsnRecord->Reason & USN_REASON_FILE_DELETE)){
|
200
|
+
rb_hash_delete(v_path_db,ULL2NUM(FRN));
|
201
|
+
}
|
202
|
+
}
|
203
|
+
|
204
|
+
szReason[0] = 0;
|
205
|
+
|
206
|
+
if(ptr->pUsnRecord->Reason & USN_REASON_DATA_OVERWRITE)
|
207
|
+
add_reason(szReason, "data_overwrite");
|
208
|
+
|
209
|
+
if(ptr->pUsnRecord->Reason & USN_REASON_DATA_EXTEND)
|
210
|
+
add_reason(szReason, "data_extend");
|
211
|
+
|
212
|
+
if(ptr->pUsnRecord->Reason & USN_REASON_DATA_TRUNCATION)
|
213
|
+
add_reason(szReason, "data_truncation");
|
214
|
+
|
215
|
+
if(ptr->pUsnRecord->Reason & USN_REASON_NAMED_DATA_OVERWRITE)
|
216
|
+
add_reason(szReason, "named_data_overwrite");
|
217
|
+
|
218
|
+
if(ptr->pUsnRecord->Reason & USN_REASON_NAMED_DATA_EXTEND)
|
219
|
+
add_reason(szReason, "named_data_extend");
|
220
|
+
|
221
|
+
if(ptr->pUsnRecord->Reason & USN_REASON_NAMED_DATA_TRUNCATION)
|
222
|
+
add_reason(szReason, "named_data_truncation");
|
223
|
+
|
224
|
+
if(ptr->pUsnRecord->Reason & USN_REASON_FILE_CREATE)
|
225
|
+
add_reason(szReason, "file_create");
|
226
|
+
|
227
|
+
if(ptr->pUsnRecord->Reason & USN_REASON_FILE_DELETE)
|
228
|
+
add_reason(szReason, "file_delete");
|
229
|
+
|
230
|
+
#ifdef USN_REASON_PROPERTY_CHANGE
|
231
|
+
if(ptr->pUsnRecord->Reason & USN_REASON_PROPERTY_CHANGE)
|
232
|
+
add_reason(szReason, "property_change");
|
233
|
+
#endif
|
234
|
+
|
235
|
+
#ifdef USN_REASON_EA_CHANGE
|
236
|
+
if(ptr->pUsnRecord->Reason & USN_REASON_EA_CHANGE)
|
237
|
+
add_reason(szReason, "property_change");
|
238
|
+
#endif
|
239
|
+
|
240
|
+
if(ptr->pUsnRecord->Reason & USN_REASON_SECURITY_CHANGE)
|
241
|
+
add_reason(szReason, "security_change");
|
242
|
+
|
243
|
+
if(ptr->pUsnRecord->Reason & USN_REASON_RENAME_OLD_NAME)
|
244
|
+
add_reason(szReason, "rename_old_name");
|
245
|
+
|
246
|
+
if(ptr->pUsnRecord->Reason & USN_REASON_RENAME_NEW_NAME)
|
247
|
+
add_reason(szReason, "rename_new_name");
|
248
|
+
|
249
|
+
if(ptr->pUsnRecord->Reason & USN_REASON_INDEXABLE_CHANGE)
|
250
|
+
add_reason(szReason, "indexable_change");
|
251
|
+
|
252
|
+
if(ptr->pUsnRecord->Reason & USN_REASON_BASIC_INFO_CHANGE)
|
253
|
+
add_reason(szReason, "basic_info_change");
|
254
|
+
|
255
|
+
if(ptr->pUsnRecord->Reason & USN_REASON_HARD_LINK_CHANGE)
|
256
|
+
add_reason(szReason, "hard_link_change");
|
257
|
+
|
258
|
+
if(ptr->pUsnRecord->Reason & USN_REASON_COMPRESSION_CHANGE)
|
259
|
+
add_reason(szReason, "compression_change");
|
260
|
+
|
261
|
+
if(ptr->pUsnRecord->Reason & USN_REASON_ENCRYPTION_CHANGE)
|
262
|
+
add_reason(szReason, "encryption_change");
|
263
|
+
|
264
|
+
if(ptr->pUsnRecord->Reason & USN_REASON_OBJECT_ID_CHANGE)
|
265
|
+
add_reason(szReason, "object_id_change");
|
266
|
+
|
267
|
+
if(ptr->pUsnRecord->Reason & USN_REASON_REPARSE_POINT_CHANGE)
|
268
|
+
add_reason(szReason, "reparse_point_change");
|
269
|
+
|
270
|
+
#ifdef USN_REASON_MOUNT_TABLE_CHANGE
|
271
|
+
if(ptr->pUsnRecord->Reason & USN_REASON_MOUNT_TABLE_CHANGE)
|
272
|
+
add_reason(szReason, "stream_change");
|
273
|
+
|
274
|
+
#endif
|
275
|
+
#ifdef USN_REASON_STREAM_CHANGE
|
276
|
+
if(ptr->pUsnRecord->Reason & USN_REASON_STREAM_CHANGE)
|
277
|
+
add_reason(szReason, "stream_change");
|
278
|
+
#endif
|
279
|
+
|
280
|
+
if(ptr->pUsnRecord->Reason & USN_REASON_CLOSE)
|
281
|
+
add_reason(szReason, "close");
|
282
|
+
|
283
|
+
v_action = rb_str_new2(szReason);
|
284
|
+
|
285
|
+
*path = '\0';
|
286
|
+
|
287
|
+
FRN = ptr->pUsnRecord->ParentFileReferenceNumber;
|
288
|
+
v_path_array = rb_ary_new();
|
289
|
+
|
290
|
+
while(FRN != 0) {
|
291
|
+
v_arr = rb_hash_aref(v_path_db,ULL2NUM(FRN));
|
292
|
+
|
293
|
+
if(NIL_P(v_arr))
|
294
|
+
break;
|
295
|
+
|
296
|
+
v_path = RARRAY(v_arr)->ptr[0];
|
297
|
+
rb_ary_unshift(v_path_array, v_path);
|
298
|
+
FRN = NUM2ULL(RARRAY(rb_hash_aref(v_path_db, ULL2NUM(FRN)))->ptr[1]);
|
299
|
+
}
|
300
|
+
|
301
|
+
v_path = rb_ary_join(v_path_array, rb_str_new("\\", 1));
|
302
|
+
strcpy(path, StringValuePtr(v_path));
|
303
|
+
|
304
|
+
WideCharToMultiByte(
|
305
|
+
CP_ACP,
|
306
|
+
0,
|
307
|
+
szFile,
|
308
|
+
-1,
|
309
|
+
file_name,
|
310
|
+
MAX_PATH,
|
311
|
+
NULL,
|
312
|
+
NULL
|
313
|
+
);
|
314
|
+
|
315
|
+
v_struct = rb_struct_new(
|
316
|
+
sChangeJournalStruct,
|
317
|
+
v_action,
|
318
|
+
rb_str_new2(file_name),
|
319
|
+
rb_str_new2(path)
|
320
|
+
);
|
321
|
+
|
322
|
+
// This is read-only information
|
323
|
+
rb_obj_freeze(v_struct);
|
324
|
+
|
325
|
+
rb_ary_push(v_array, v_struct);
|
326
|
+
}
|
327
|
+
|
328
|
+
return v_array;
|
329
|
+
}
|
330
|
+
|
331
|
+
// Return an error code as a string
|
332
|
+
LPTSTR ErrorDescription(DWORD p_dwError)
|
333
|
+
{
|
334
|
+
HLOCAL hLocal = NULL;
|
335
|
+
static char ErrStr[MAX_STRING];
|
336
|
+
int len;
|
337
|
+
|
338
|
+
if (!(len=FormatMessage(
|
339
|
+
FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
340
|
+
FORMAT_MESSAGE_FROM_SYSTEM |
|
341
|
+
FORMAT_MESSAGE_IGNORE_INSERTS,
|
342
|
+
NULL,
|
343
|
+
p_dwError,
|
344
|
+
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
|
345
|
+
(LPTSTR)&hLocal,
|
346
|
+
0,
|
347
|
+
NULL)))
|
348
|
+
{
|
349
|
+
rb_raise(rb_eStandardError, "Unable to format error message");
|
350
|
+
}
|
351
|
+
|
352
|
+
memset(ErrStr, 0, MAX_STRING);
|
353
|
+
strncpy(ErrStr, (LPTSTR)hLocal, len-2); // remove \r\n
|
354
|
+
LocalFree(hLocal);
|
355
|
+
return ErrStr;
|
356
|
+
}
|
@@ -1,28 +1,33 @@
|
|
1
1
|
#############################################################################
|
2
|
-
#
|
2
|
+
# test_win32_changejournal.rb
|
3
3
|
#
|
4
|
-
# Test suite for the win32-changejournal
|
4
|
+
# Test suite for the win32-changejournal library. You should run this
|
5
5
|
# via the 'rake test' task.
|
6
6
|
#############################################################################
|
7
|
+
require 'rubygems'
|
8
|
+
gem 'test-unit'
|
9
|
+
|
7
10
|
require 'test/unit'
|
8
11
|
require 'win32/changejournal'
|
9
12
|
include Win32
|
10
13
|
|
11
|
-
STDOUT.print "\n\nThis may take a few seconds - be patient\n\n"
|
12
|
-
|
13
14
|
class TC_Win32_ChangeJournal < Test::Unit::TestCase
|
14
|
-
|
15
|
+
def self.startup
|
16
|
+
Dir.chdir(File.expand_path(File.dirname(__FILE__)))
|
17
|
+
@@file = 'win32_changejournal_test.txt'
|
18
|
+
end
|
19
|
+
|
15
20
|
def setup
|
21
|
+
# The thread is used to force an event to happen for the tests
|
16
22
|
@journal = ChangeJournal.new("c:\\")
|
17
|
-
@
|
18
|
-
@thread = Thread.new{
|
23
|
+
@thread = Thread.new{
|
19
24
|
sleep 2
|
20
|
-
File.open(
|
25
|
+
File.open(@@file, 'w'){ |fh| fh.puts 'Delete me!' }
|
21
26
|
}
|
22
27
|
end
|
23
28
|
|
24
29
|
def test_version
|
25
|
-
assert_equal('0.3.
|
30
|
+
assert_equal('0.3.3', ChangeJournal::VERSION)
|
26
31
|
end
|
27
32
|
|
28
33
|
def test_changejournal_action
|
@@ -31,6 +36,7 @@ class TC_Win32_ChangeJournal < Test::Unit::TestCase
|
|
31
36
|
assert_kind_of(Array, c)
|
32
37
|
assert_kind_of(Struct::ChangeJournalStruct, c.first)
|
33
38
|
assert_equal(['action', 'file_name', 'path'], c.first.members)
|
39
|
+
assert_true(c.first.frozen?)
|
34
40
|
}
|
35
41
|
end
|
36
42
|
|
@@ -39,7 +45,7 @@ class TC_Win32_ChangeJournal < Test::Unit::TestCase
|
|
39
45
|
assert_nothing_raised{ @journal.delete }
|
40
46
|
end
|
41
47
|
|
42
|
-
# We provide some very short timeouts here - shouldn't slow the tests down
|
48
|
+
# We provide some very short timeouts here - shouldn't slow the tests down
|
43
49
|
def test_wait_basic
|
44
50
|
assert_respond_to(@journal, :wait)
|
45
51
|
assert_nothing_raised{ @journal.wait(0.01) }
|
@@ -50,11 +56,20 @@ class TC_Win32_ChangeJournal < Test::Unit::TestCase
|
|
50
56
|
assert_raise(ArgumentError){ @journal.wait(1,1) }
|
51
57
|
assert_raise(TypeError){ @journal.wait('a') }
|
52
58
|
end
|
59
|
+
|
60
|
+
def test_error_class_defined
|
61
|
+
assert_kind_of(Object, Win32::ChangeJournal::Error)
|
62
|
+
end
|
53
63
|
|
54
64
|
def teardown
|
55
|
-
@thread.kill
|
56
|
-
|
65
|
+
@thread.kill if @thread.alive?
|
66
|
+
|
57
67
|
@journal = nil
|
58
68
|
@flags = nil
|
69
|
+
@thread = nil
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.shutdown
|
73
|
+
File.delete(@@file) if File.exists?(@@file)
|
59
74
|
end
|
60
75
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
|
3
|
+
spec = Gem::Specification.new do |gem|
|
4
|
+
gem.name = 'win32-changejournal'
|
5
|
+
gem.version = '0.3.3'
|
6
|
+
gem.authors = ['Daniel J. Berger', 'Park Heesob']
|
7
|
+
gem.license = 'Artistic 2.0'
|
8
|
+
gem.email = 'djberg96@gmail.com'
|
9
|
+
gem.homepage = 'http://www.rubyforge.org/projects/win32utils'
|
10
|
+
gem.platform = Gem::Platform::RUBY
|
11
|
+
gem.summary = 'A library for monitoring files and directories on NTFS'
|
12
|
+
gem.has_rdoc = true
|
13
|
+
gem.test_file = 'test/test_win32_changejournal.rb'
|
14
|
+
gem.extensions = ['ext/extconf.rb']
|
15
|
+
gem.files = Dir['**/*'].reject{ |f| f.include?('CVS') }
|
16
|
+
|
17
|
+
gem.required_ruby_version = '>= 1.8.2'
|
18
|
+
gem.rubyforge_project = 'win32utils'
|
19
|
+
|
20
|
+
gem.extra_rdoc_files = [
|
21
|
+
'README',
|
22
|
+
'CHANGES',
|
23
|
+
'MANIFEST',
|
24
|
+
'ext/win32/changejournal.c'
|
25
|
+
]
|
26
|
+
|
27
|
+
gem.add_development_dependency('test-unit', '>= 2.0.3')
|
28
|
+
|
29
|
+
gem.description = <<-EOF
|
30
|
+
The win32-changejournal library provides an interface for MS Windows
|
31
|
+
change journals. A change journal is a record of any changes on a given
|
32
|
+
volume maintained by the operating system itself.
|
33
|
+
EOF
|
34
|
+
end
|
35
|
+
|
36
|
+
Gem::Builder.new(spec).build
|
metadata
CHANGED
@@ -1,39 +1,54 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: win32-changejournal
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Daniel J. Berger
|
8
|
+
- Park Heesob
|
8
9
|
autorequire:
|
9
10
|
bindir: bin
|
10
11
|
cert_chain: []
|
11
12
|
|
12
|
-
date:
|
13
|
+
date: 2009-08-18 00:00:00 -06:00
|
13
14
|
default_executable:
|
14
|
-
dependencies:
|
15
|
-
|
16
|
-
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: test-unit
|
18
|
+
type: :development
|
19
|
+
version_requirement:
|
20
|
+
version_requirements: !ruby/object:Gem::Requirement
|
21
|
+
requirements:
|
22
|
+
- - ">="
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: 2.0.3
|
25
|
+
version:
|
26
|
+
description: " The win32-changejournal library provides an interface for MS Windows\n change journals. A change journal is a record of any changes on a given\n volume maintained by the operating system itself.\n"
|
17
27
|
email: djberg96@gmail.com
|
18
28
|
executables: []
|
19
29
|
|
20
|
-
extensions:
|
21
|
-
|
30
|
+
extensions:
|
31
|
+
- ext/extconf.rb
|
22
32
|
extra_rdoc_files:
|
23
33
|
- README
|
24
34
|
- CHANGES
|
25
35
|
- MANIFEST
|
26
36
|
- ext/win32/changejournal.c
|
27
37
|
files:
|
28
|
-
- lib/win32
|
29
|
-
- lib/win32/changejournal.so
|
30
|
-
- test/tc_changejournal.rb
|
31
|
-
- README
|
32
38
|
- CHANGES
|
33
|
-
-
|
39
|
+
- examples/example_changejournal.rb
|
40
|
+
- ext/extconf.rb
|
34
41
|
- ext/win32/changejournal.c
|
42
|
+
- ext/win32/changejournal.h
|
43
|
+
- MANIFEST
|
44
|
+
- Rakefile
|
45
|
+
- README
|
46
|
+
- test/test_win32_changejournal.rb
|
47
|
+
- win32-changejournal.gemspec
|
35
48
|
has_rdoc: true
|
36
49
|
homepage: http://www.rubyforge.org/projects/win32utils
|
50
|
+
licenses:
|
51
|
+
- Artistic 2.0
|
37
52
|
post_install_message:
|
38
53
|
rdoc_options: []
|
39
54
|
|
@@ -43,7 +58,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
43
58
|
requirements:
|
44
59
|
- - ">="
|
45
60
|
- !ruby/object:Gem::Version
|
46
|
-
version: 1.8.
|
61
|
+
version: 1.8.2
|
47
62
|
version:
|
48
63
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
49
64
|
requirements:
|
@@ -54,9 +69,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
54
69
|
requirements: []
|
55
70
|
|
56
71
|
rubyforge_project: win32utils
|
57
|
-
rubygems_version: 1.
|
72
|
+
rubygems_version: 1.3.5
|
58
73
|
signing_key:
|
59
|
-
specification_version:
|
74
|
+
specification_version: 3
|
60
75
|
summary: A library for monitoring files and directories on NTFS
|
61
76
|
test_files:
|
62
|
-
- test/
|
77
|
+
- test/test_win32_changejournal.rb
|
data/lib/win32/changejournal.so
DELETED
Binary file
|