icfs 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/lib/icfs/store_fs.rb CHANGED
@@ -57,6 +57,16 @@ class StoreFs < Store
57
57
  end
58
58
 
59
59
 
60
+ ###############################################
61
+ # (see Store#file_size)
62
+ #
63
+ def file_size(cid, enum, lnum, fnum)
64
+ File.size(_file(cid, enum, lnum, fnum))
65
+ rescue Errno::ENOENT
66
+ return nil
67
+ end
68
+
69
+
60
70
  ###############################################
61
71
  # (see Store#tempfile)
62
72
  #
data/lib/icfs/store_s3.rb CHANGED
@@ -64,6 +64,17 @@ class StoreS3 < Store
64
64
  end
65
65
 
66
66
 
67
+ ###############################################
68
+ # (see Store#file_size)
69
+ def file_size(cid, enum, lnum, fnum)
70
+ key = _file(cid, enum, lnum, fnum)
71
+ resp = @s3.head_object( bucket: @bck, key: key )
72
+ return resp.content_length
73
+ rescue Aws::S3::Errors::NotFound
74
+ return nil
75
+ end
76
+
77
+
67
78
  ###############################################
68
79
  # (see Store#tempfile)
69
80
  #
data/lib/icfs/users.rb CHANGED
@@ -19,6 +19,8 @@ module ICFS
19
19
  #
20
20
  # @abstract
21
21
  #
22
+ # @todo Add cache flush method
23
+ #
22
24
  class Users
23
25
 
24
26
  ###############################################
@@ -17,6 +17,8 @@ module ICFS
17
17
  ##########################################################################
18
18
  # Implements {ICFS::Users Users} using Elasticsearch to cache
19
19
  # details from another {ICFS::Users Users} instance.
20
+ #
21
+ # @todo Add logging
20
22
  #
21
23
  class UsersElastic < Users
22
24
 
@@ -120,12 +122,12 @@ class UsersElastic < Users
120
122
  obj['active'] = true
121
123
 
122
124
  # store in cache
123
- json = Validate.generate(obj, 'User/Role/Group'.freeze, ValUserCache)
125
+ json = Items.generate(obj, 'User/Role/Group'.freeze, ValUserCache)
124
126
  _write(:users, urg, json)
125
127
 
126
128
  # use cached version
127
129
  else
128
- obj = Validate.parse(json, 'User/Role/Group'.freeze, ValUserCache)
130
+ obj = Items.parse(json, 'User/Role/Group'.freeze, ValUserCache)
129
131
  end
130
132
 
131
133
  # expired
@@ -146,7 +148,7 @@ class UsersElastic < Users
146
148
  obj['last'] = now
147
149
 
148
150
  # and store in cache
149
- json = Validate.generate(obj, 'User/Role/Group'.freeze, ValUserCache)
151
+ json = Items.generate(obj, 'User/Role/Group'.freeze, ValUserCache)
150
152
  _write(:users, urg, json)
151
153
  end
152
154
 
data/lib/icfs/users_fs.rb CHANGED
@@ -26,7 +26,7 @@ class UsersFs < Users
26
26
  # read a raw file
27
27
  def _read(fn)
28
28
  json = File.read(File.join(@path, fn + '.json'.freeze))
29
- obj = Validate.parse(json, 'User/Role/Group'.freeze, Users::ValUser)
29
+ obj = Items.parse(json, 'User/Role/Group'.freeze, Users::ValUser)
30
30
  if obj['name'] != fn
31
31
  raise(Error::Values, 'UsersFs user %s name mismatch'.freeze % fn)
32
32
  end
@@ -51,7 +51,7 @@ class UsersFs < Users
51
51
  # (see Users#read)
52
52
  #
53
53
  def read(urg)
54
- Validate.validate(urg, 'User/Role/Group'.freeze, Items::FieldUsergrp)
54
+ Items.validate(urg, 'User/Role/Group'.freeze, Items::FieldUsergrp)
55
55
 
56
56
  # get the base user
57
57
  usr = _read(urg)
@@ -113,7 +113,7 @@ class UsersFs < Users
113
113
  # (see Users#write)
114
114
  #
115
115
  def write(obj)
116
- Validate.validate(obj, 'User/Role/Group'.freeze, Users::ValUser)
116
+ Items.validate(obj, 'User/Role/Group'.freeze, Users::ValUser)
117
117
  json = JSON.pretty_generate(obj)
118
118
 
119
119
  # write to temp file
@@ -0,0 +1,177 @@
1
+ #
2
+ # Investigative Case File System
3
+ #
4
+ # Copyright 2019 by Graham A. Field
5
+ #
6
+ # This program is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License version 3.
8
+ #
9
+ # This program is distributed WITHOUT ANY WARRANTY; without even the
10
+ # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11
+
12
+ require_relative '../store_fs'
13
+ #
14
+ module ICFS
15
+
16
+ module Utils
17
+
18
+ ##########################################################################
19
+ # Backup and restore utilities
20
+ #
21
+ class Backup
22
+
23
+ ###############################################
24
+ # Instance
25
+ #
26
+ # @param cache [Cache] The live cache
27
+ # @param store [Store] The live store
28
+ # @param log [Logger] Where to log
29
+ #
30
+ def initialize(cache, store, log)
31
+ @cache = cache
32
+ @store = store
33
+ @log = log
34
+ end
35
+
36
+
37
+ ###############################################
38
+ # Copy an item
39
+ #
40
+ def _item(dest, title, read, write, args, val=nil)
41
+ @log.debug('ICFS copy: %s'.freeze % title)
42
+
43
+ # read the item
44
+ json = @store.send(read, *args)
45
+ if !json
46
+ @log.warn('ICFS copy: %s is missing'.freeze % title)
47
+ return nil
48
+ end
49
+
50
+ # parse the item if requested
51
+ if val
52
+ obj = JSON.parse(json)
53
+ err = Validate.check(obj, val)
54
+ if err
55
+ @log.error('ICFS copy: %s bad format'.freeze % title)
56
+ return nil
57
+ end
58
+ end
59
+
60
+ # write the item
61
+ dest.send(write, *args, json)
62
+ return obj
63
+
64
+ rescue JSON::ParserError
65
+ @log.error('ICFS copy: %s bad JSON'.freeze % title)
66
+ return nil
67
+ end # def _item
68
+ private :_item
69
+
70
+
71
+ ###############################################
72
+ # Transfer a case to another store
73
+ #
74
+ # @param cid [String] The case ID
75
+ # @param dest [Store] The destination store
76
+ # @param lnum_max [Integer] The highest log
77
+ # @param lnum_min [Integer] The lowest log
78
+ #
79
+ def copy(cid, dest, lnum_max, lnum_min=1)
80
+ @log.info('ICFS copy: %s %d-%d'.freeze % [cid, lnum_min, lnum_max])
81
+ if lnum_min > lnum_max
82
+ raise ArgumentError, 'ICFS copy, log num min is larger than max'.freeze
83
+ end
84
+
85
+ # each log
86
+ lnum = lnum_min
87
+ while lnum <= lnum_max
88
+
89
+ # copy the log
90
+ log = _copy_item(dest,
91
+ 'log %d'.freeze % lnum,
92
+ :log_read,
93
+ :log_write,
94
+ [cid, lnum],
95
+ Items::ItemLog
96
+ )
97
+ if !log
98
+ lnum += 1
99
+ next
100
+ end
101
+
102
+ # entry
103
+ enum = log['entry']['num']
104
+ _copy_item(dest,
105
+ 'entry %d-%d'.freeze % [enum, lnum],
106
+ :entry_read,
107
+ :entry_write,
108
+ [cid, lnum]
109
+ )
110
+
111
+ # index
112
+ if log['index']
113
+ xnum = log['index']['num']
114
+ _copy_item(dest,
115
+ 'index %d-%d'.freeze % [xnum, lnum],
116
+ :index_read,
117
+ :index_write,
118
+ [cid, xnum, lnum]
119
+ )
120
+ end
121
+
122
+ # action
123
+ if log['action']
124
+ anum = log['action']['lnum']
125
+ _copy_item(dest,
126
+ 'action %d-%d'.freeze % [anum, lnum],
127
+ :action_read,
128
+ :action_write,
129
+ [cid, anum, lnum]
130
+ )
131
+ end
132
+
133
+ # case
134
+ if log['case_hash']
135
+ _copy_item(dest,
136
+ 'case %d'.freeze, % lnum,
137
+ :case_read,
138
+ :case_write,
139
+ [cid, lnum]
140
+ )
141
+ end
142
+
143
+ # files
144
+ if log['files_hash']
145
+ log['files_hash'].each_index do |fnum|
146
+
147
+ @log.debug('ICFS copy: file %d-%d-%d'.freeze % [enum, lnum, fnum])
148
+
149
+ # read
150
+ fi = @store.file_read(cid, enum, lnum, fnum)
151
+ if !fi
152
+ @log.warn('ICFS copy: file %d-%d-%d missing'.freeze %
153
+ [enum, lnum, fnum])
154
+ next
155
+ end
156
+
157
+ # copy
158
+ tmp = dest.tempfile
159
+ IO.copy_stream(fi, tmp)
160
+ @store.close(fi)
161
+
162
+ # write
163
+ dest.file_write(cid, enum, lnum, fnum, tmp)
164
+ end
165
+ end
166
+
167
+ lnum += 1
168
+ end
169
+
170
+ end # def copy()
171
+
172
+
173
+ end # class ICFS::Utils::Backup
174
+
175
+ end # module ICFS::Utils
176
+
177
+ end # module ICFS
@@ -0,0 +1,290 @@
1
+ #
2
+ # Investigative Case File System
3
+ #
4
+ # Copyright 2019 by Graham A. Field
5
+ #
6
+ # This program is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License version 3.
8
+ #
9
+ # This program is distributed WITHOUT ANY WARRANTY; without even the
10
+ # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11
+
12
+ #
13
+ module ICFS
14
+
15
+
16
+ ##########################################################################
17
+ # Low level utilities for working directly with ICFS systems.
18
+ #
19
+ # @todo Archive/move case tool written
20
+ #
21
+ module Utils
22
+
23
+
24
+ ##########################################################################
25
+ # Check a case for errors
26
+ #
27
+ # @todo Make a function to auto find the last log
28
+ #
29
+ class Check
30
+
31
+
32
+ ###############################################
33
+ # Instance
34
+ #
35
+ # @param store [Store] The store to check
36
+ # @param log [Logger] Where to log
37
+ #
38
+ def initialize(store, log)
39
+ @store = store
40
+ @log = log
41
+ end
42
+
43
+
44
+ ###############################################
45
+ # check an object
46
+ #
47
+ def _item(title, read, read_args, hist, hash, val, con)
48
+
49
+ @log.debug('ICFS check: %s'.freeze % title)
50
+
51
+ # read
52
+ json = @store.send(read, *read_args)
53
+ if !json
54
+ if hist
55
+ @log.warn('ICFS check: %s is missing and historical'.freeze % title)
56
+ else
57
+ @log.error('ICFS check: %s is missing and current'.freeze % title)
58
+ end
59
+ return nil
60
+ end
61
+
62
+ # hash
63
+ if hash
64
+ if hash != ICFS.hash(json)
65
+ @log.error('ICFS check: %s hash bad'.freeze % title)
66
+ end
67
+ else
68
+ @log.warn('ICFS check: %s hash unverified'.freeze % title)
69
+ end
70
+
71
+ # parse
72
+ obj = JSON.parse(json)
73
+ err = Validate.check(obj, val)
74
+ if err
75
+ @log.error('ICFS check: %s bad format'.freeze % title)
76
+ return nil
77
+ end
78
+
79
+ # inconsistent
80
+ con.each do |name, num|
81
+ if obj[name] != read_args[num]
82
+ @log.error('ICFS check: %s inconsistent'.freeze % title)
83
+ return nil
84
+ end
85
+ end
86
+
87
+ return obj
88
+
89
+ rescue JSON::ParserError
90
+ @log.error('ICFS check: %s bad JSON'.freeze % title)
91
+ return nil
92
+ end # def _item()
93
+ private :_item
94
+
95
+
96
+ ###############################################
97
+ # Check a case
98
+ #
99
+ # @param cur_log [Integer] The last log
100
+ # @param cur_hash [String] The hash of the last log
101
+ #
102
+ def check(cid, cur_log, cur_hash, opts={})
103
+ @log.info('ICFS check: case %s'.freeze % cid)
104
+
105
+ ent_cur = Set.new
106
+ cse_cur = false
107
+ idx_cur = Set.new
108
+ act_cur = Set.new
109
+ file_cur = Set.new
110
+
111
+
112
+ # go thru the logs from most current
113
+ lnum = cur_log
114
+ hash_log = cur_hash
115
+ time_log = Time.now.to_i
116
+ while( lnum > 0 )
117
+
118
+ # log
119
+ log = _item(
120
+ 'log %d'.freeze % lnum,
121
+ :log_read,
122
+ [cid, lnum],
123
+ false,
124
+ hash_log,
125
+ Items::ItemLog,
126
+ [
127
+ ['caseid'.freeze, 0].freeze,
128
+ ['log'.freeze, 1].freeze
129
+ ].freeze,
130
+ )
131
+ if !log
132
+ hash_log = nil
133
+ lnum = lnum - 1
134
+ next
135
+ end
136
+
137
+ # check that time decreases
138
+ if log['time'] > time_log
139
+ @log.warn('ICFS check: log %d time inconsistent'.freeze % lnum)
140
+ end
141
+
142
+ # entry
143
+ enum = log['entry']['num']
144
+ ent = _item(
145
+ 'entry %d-%d' % [enum, lnum],
146
+ :entry_read,
147
+ [cid, enum, lnum],
148
+ ent_cur.include?(enum),
149
+ log['entry']['hash'],
150
+ Items::ItemEntry,
151
+ [
152
+ ['caseid'.freeze, 0].freeze,
153
+ ['entry'.freeze, 1].freeze,
154
+ ['log'.freeze, 2].freeze
155
+ ].freeze,
156
+ )
157
+
158
+ # current entry
159
+ unless ent_cur.include?(enum)
160
+ ent_cur.add(enum)
161
+ if ent['files']
162
+ ent['files'].each do |fd|
163
+ file_cur.add( '%d-%d-%d'.freeze % [enum, fd['num'], fd['log']] )
164
+ end
165
+ end
166
+ end
167
+
168
+ # index
169
+ if log['index']
170
+ xnum = log['index']['num']
171
+ idx = _item(
172
+ 'index %d-%d'. freeze % [xnum, lnum],
173
+ :index_read,
174
+ [cid, xnum, lnum],
175
+ idx_cur.include?(xnum),
176
+ log['index']['hash'],
177
+ Items::ItemIndex,
178
+ [
179
+ ['caseid'.freeze, 0].freeze,
180
+ ['index'.freeze, 1].freeze,
181
+ ['log'.freeze, 2].freeze
182
+ ]
183
+ )
184
+ idx_cur.add(xnum)
185
+ end
186
+
187
+ # action
188
+ if log['action']
189
+ anum = log['action']['num']
190
+ act = _item(
191
+ 'action %d-%d'.freeze % [anum, lnum],
192
+ :action_read,
193
+ [cid, anum, lnum],
194
+ act_cur.include?(anum),
195
+ log['action']['hash'],
196
+ Items::ItemAction,
197
+ [
198
+ ['caseid'.freeze, 0].freeze,
199
+ ['action'.freeze, 1].freeze,
200
+ ['log'.freeze, 2].freeze
201
+ ]
202
+ )
203
+ act_cur.add(anum)
204
+ end
205
+
206
+ # case
207
+ if log['case_hash']
208
+ cse = _item(
209
+ 'case %d'.freeze % lnum,
210
+ :case_read,
211
+ [cid, lnum],
212
+ cse_cur,
213
+ log['case_hash'],
214
+ Items::ItemCase,
215
+ [
216
+ ['caseid'.freeze, 0].freeze,
217
+ ['log'.freeze, 1].freeze
218
+ ]
219
+ )
220
+ cse_cur = true
221
+ end
222
+
223
+ # files
224
+ if log['files_hash']
225
+ fnum = 0
226
+ log['files_hash'].each do |hash|
227
+ fnum = fnum + 1
228
+ fn = '%d-%d-%d'.freeze % [enum, fnum, lnum]
229
+ cur = file_cur.include?(fn)
230
+ file_cur.delete(fn) if cur
231
+
232
+ @log.debug('ICFS check: file %s'.freeze % fn)
233
+
234
+ # read/size
235
+ if opts[:hash_all] || (cur && opts[:hash_current])
236
+ fi = @store.file_read(cid, enum, lnum, fnum)
237
+ elsif opts[:stat_all] || (cur && opts[:stat_current])
238
+ fi = @store.file_size(cur, enum, lnum, fnum)
239
+ else
240
+ fi = true
241
+ end
242
+
243
+ # missing
244
+ if !fi
245
+ if cur
246
+ @log.error('ICFS check: file %s missing and current'.freeze %
247
+ fn)
248
+ else
249
+ @log.warn('ICFS check: file %s missing and historical'.freeze %
250
+ fn)
251
+ end
252
+ end
253
+
254
+ # hash
255
+ if fi.is_a?(File)
256
+ # check
257
+ if hash != ICFS.hash_temp(fi)
258
+ @log.error('ICFS check: file %s hash bad'.freeze % fn)
259
+ end
260
+
261
+ # close
262
+ if fi.respond_to?(:close!)
263
+ fi.close!
264
+ else
265
+ fi.close
266
+ end
267
+ end
268
+ end
269
+ end
270
+
271
+ # previous log
272
+ lnum = lnum - 1
273
+ hash_log = log['prev']
274
+ end
275
+
276
+ # check for any non-existant current files
277
+ unless file_cur.empty?
278
+ file_cur.each do |fn|
279
+ @log.error('ICFS check: file %s current but not logged'.freeze % fn)
280
+ end
281
+ end
282
+
283
+ @log.debug('ICFS check: case %s complete'.freeze % cid)
284
+ end # def check()
285
+
286
+
287
+ end # class ICFS::Utils::Check
288
+
289
+ end # module ICFS::Utils
290
+ end # module ICFS