icfs 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/data/icfs.css +1 -0
- data/{bin/icfs_dev_todo.rb → devel/create.sh} +4 -9
- data/devel/elastic.sh +20 -0
- data/devel/icfs-wrk/Dockerfile +27 -0
- data/devel/minio.sh +22 -0
- data/devel/redis.sh +18 -0
- data/devel/server.sh +18 -0
- data/devel/test/es-s3-fs-init.rb +93 -0
- data/devel/test/es-s3-fs-webrick.rb +79 -0
- data/devel/test/es-s3-restore.rb +56 -0
- data/devel/test/init.rb +93 -0
- data/devel/test/run.rb +96 -0
- data/lib/icfs/api.rb +22 -11
- data/lib/icfs/cache_elastic.rb +9 -1
- data/lib/icfs/demo/auth.rb +7 -1
- data/lib/icfs/store.rb +6 -12
- data/lib/icfs/store_fs.rb +5 -1
- data/lib/icfs/users.rb +0 -3
- data/lib/icfs/users_fs.rb +19 -70
- data/lib/icfs/users_redis.rb +127 -0
- data/lib/icfs/users_s3.rb +68 -0
- data/lib/icfs/utils/backup.rb +212 -7
- data/lib/icfs/utils/check.rb +1 -5
- data/lib/icfs/web/client.rb +101 -8
- data/lib/icfs/web/config.rb +108 -0
- data/lib/icfs/web/config_redis.rb +78 -0
- data/lib/icfs/web/config_s3.rb +66 -0
- data/lib/icfs.rb +0 -2
- metadata +18 -4
- data/lib/icfs/users_elastic.rb +0 -168
data/lib/icfs/store.rb
CHANGED
@@ -220,8 +220,7 @@ class Store
|
|
220
220
|
# Path for case
|
221
221
|
#
|
222
222
|
def _case(cid, lnum)
|
223
|
-
[
|
224
|
-
@base,
|
223
|
+
@base + [
|
225
224
|
cid,
|
226
225
|
'c'.freeze,
|
227
226
|
'%d.json'.freeze % lnum
|
@@ -233,8 +232,7 @@ class Store
|
|
233
232
|
# Path for log
|
234
233
|
#
|
235
234
|
def _log(cid, lnum)
|
236
|
-
[
|
237
|
-
@base,
|
235
|
+
@base + [
|
238
236
|
cid,
|
239
237
|
'l'.freeze,
|
240
238
|
'%d.json'.freeze % lnum
|
@@ -246,8 +244,7 @@ class Store
|
|
246
244
|
# Path for entry
|
247
245
|
#
|
248
246
|
def _entry(cid, enum, lnum)
|
249
|
-
[
|
250
|
-
@base,
|
247
|
+
@base + [
|
251
248
|
cid,
|
252
249
|
'e'.freeze,
|
253
250
|
enum.to_s,
|
@@ -260,8 +257,7 @@ class Store
|
|
260
257
|
# Path for file
|
261
258
|
#
|
262
259
|
def _file(cid, enum, lnum, fnum)
|
263
|
-
[
|
264
|
-
@base,
|
260
|
+
@base + [
|
265
261
|
cid,
|
266
262
|
'e'.freeze,
|
267
263
|
enum.to_s,
|
@@ -274,8 +270,7 @@ class Store
|
|
274
270
|
# Filename for action
|
275
271
|
#
|
276
272
|
def _action(cid, anum, lnum)
|
277
|
-
[
|
278
|
-
@base,
|
273
|
+
@base + [
|
279
274
|
cid,
|
280
275
|
'a'.freeze,
|
281
276
|
anum.to_s,
|
@@ -288,8 +283,7 @@ class Store
|
|
288
283
|
# Filename for index
|
289
284
|
#
|
290
285
|
def _index(cid, xnum, lnum)
|
291
|
-
[
|
292
|
-
@base,
|
286
|
+
@base + [
|
293
287
|
cid,
|
294
288
|
'i'.freeze,
|
295
289
|
xnum.to_s,
|
data/lib/icfs/store_fs.rb
CHANGED
data/lib/icfs/users.rb
CHANGED
@@ -16,11 +16,8 @@ module ICFS
|
|
16
16
|
##########################################################################
|
17
17
|
# User, Role, Group, and Global Perms
|
18
18
|
#
|
19
|
-
#
|
20
19
|
# @abstract
|
21
20
|
#
|
22
|
-
# @todo Add cache flush method
|
23
|
-
#
|
24
21
|
class Users
|
25
22
|
|
26
23
|
###############################################
|
data/lib/icfs/users_fs.rb
CHANGED
@@ -11,7 +11,9 @@
|
|
11
11
|
|
12
12
|
require 'tempfile'
|
13
13
|
require 'fileutils'
|
14
|
-
require '
|
14
|
+
require 'json'
|
15
|
+
|
16
|
+
require_relative 'users'
|
15
17
|
|
16
18
|
module ICFS
|
17
19
|
|
@@ -20,22 +22,6 @@ module ICFS
|
|
20
22
|
#
|
21
23
|
class UsersFs < Users
|
22
24
|
|
23
|
-
private
|
24
|
-
|
25
|
-
###############################################
|
26
|
-
# read a raw file
|
27
|
-
def _read(fn)
|
28
|
-
json = File.read(File.join(@path, fn + '.json'.freeze))
|
29
|
-
obj = Items.parse(json, 'User/Role/Group'.freeze, Users::ValUser)
|
30
|
-
if obj['name'] != fn
|
31
|
-
raise(Error::Values, 'UsersFs user %s name mismatch'.freeze % fn)
|
32
|
-
end
|
33
|
-
return obj
|
34
|
-
end # _read
|
35
|
-
|
36
|
-
|
37
|
-
public
|
38
|
-
|
39
25
|
|
40
26
|
###############################################
|
41
27
|
# New instance
|
@@ -47,63 +33,26 @@ class UsersFs < Users
|
|
47
33
|
end
|
48
34
|
|
49
35
|
|
36
|
+
###############################################
|
37
|
+
# Path to store the file
|
38
|
+
#
|
39
|
+
def _path(urg)
|
40
|
+
File.join(@path, urg + '.json'.freeze)
|
41
|
+
end
|
42
|
+
private :_path
|
43
|
+
|
44
|
+
|
50
45
|
###############################################
|
51
46
|
# (see Users#read)
|
52
47
|
#
|
53
48
|
def read(urg)
|
54
|
-
Items.validate(urg, 'User/Role/Group'.freeze, Items::FieldUsergrp)
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
# assemble
|
61
|
-
type = usr['type']
|
62
|
-
done_s = Set.new.add(urg)
|
63
|
-
ary = []
|
64
|
-
roles_s = Set.new
|
65
|
-
if usr['roles']
|
66
|
-
ary.concat usr['roles']
|
67
|
-
roles_s.merge usr['roles']
|
68
|
-
end
|
69
|
-
grps_s = Set.new
|
70
|
-
if usr['groups']
|
71
|
-
ary.concat usr['groups']
|
72
|
-
grps_s.merge usr['groups']
|
73
|
-
end
|
74
|
-
perms_s = Set.new
|
75
|
-
if usr['perms']
|
76
|
-
perms_s.merge usr['perms']
|
77
|
-
end
|
78
|
-
|
79
|
-
# roles & groups
|
80
|
-
while itm = ary.shift
|
81
|
-
next if done_s.include?(itm)
|
82
|
-
done_s.add(itm)
|
83
|
-
|
84
|
-
usr = _read(itm)
|
85
|
-
if usr['roles']
|
86
|
-
ary.concat usr['roles']
|
87
|
-
roles_s.merge usr['roles']
|
88
|
-
end
|
89
|
-
if usr['groups']
|
90
|
-
ary.concat usr['groups']
|
91
|
-
grps_s.merge usr['groups']
|
92
|
-
end
|
93
|
-
if usr['perms']
|
94
|
-
perms_s.merge usr['perms']
|
95
|
-
end
|
49
|
+
Items.validate(urg, 'User/Role/Group name'.freeze, Items::FieldUsergrp)
|
50
|
+
json = File.read(_path(urg))
|
51
|
+
obj = Items.parse(json, 'User/Role/Group'.freeze, Users::ValUser)
|
52
|
+
if obj['name'] != urg
|
53
|
+
raise(Error::Values, 'UsersFs user %s name mismatch'.freeze % fn)
|
96
54
|
end
|
97
|
-
|
98
|
-
# assemble final
|
99
|
-
return {
|
100
|
-
'name' => urg.dup,
|
101
|
-
'type' => type,
|
102
|
-
'roles' => roles_s.to_a,
|
103
|
-
'groups' => grps_s.to_a,
|
104
|
-
'perms' => perms_s.to_a,
|
105
|
-
}
|
106
|
-
|
55
|
+
return obj
|
107
56
|
rescue Errno::ENOENT
|
108
57
|
return nil
|
109
58
|
end # def read()
|
@@ -122,7 +71,7 @@ class UsersFs < Users
|
|
122
71
|
tmp.close
|
123
72
|
|
124
73
|
# move
|
125
|
-
FileUtils.mv(tmp.path,
|
74
|
+
FileUtils.mv(tmp.path, _path(obj['name']))
|
126
75
|
tmp.unlink
|
127
76
|
end # def write()
|
128
77
|
|
@@ -0,0 +1,127 @@
|
|
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 'redis'
|
13
|
+
|
14
|
+
module ICFS
|
15
|
+
|
16
|
+
##########################################################################
|
17
|
+
# Implement Users with a Redis cache
|
18
|
+
#
|
19
|
+
class UsersRedis < Users
|
20
|
+
|
21
|
+
###############################################
|
22
|
+
# New instance
|
23
|
+
#
|
24
|
+
# @param redis [Redis] The redis client
|
25
|
+
# @param base [Users] The base Users store
|
26
|
+
# @param opts [Hash] Options
|
27
|
+
# @option opts [String] :prefix Prefix for Redis key
|
28
|
+
# @option opts [Integer] :expires Expiration time in seconds
|
29
|
+
#
|
30
|
+
def initialize(redis, base, opts={})
|
31
|
+
@redis = redis
|
32
|
+
@base = base
|
33
|
+
@pre = opts[:prefix] || ''.freeze
|
34
|
+
@exp = opts[:expires] || 1*60*60 # 1 hour default
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
###############################################
|
39
|
+
# Where to store in Redis
|
40
|
+
#
|
41
|
+
def _key(urg)
|
42
|
+
@pre + urg
|
43
|
+
end
|
44
|
+
private :_key
|
45
|
+
|
46
|
+
|
47
|
+
###############################################
|
48
|
+
# (see Users#read)
|
49
|
+
#
|
50
|
+
def read(urg)
|
51
|
+
Validate.check(urg, Items::FieldUsergrp) # FIXME
|
52
|
+
key = _key(urg)
|
53
|
+
|
54
|
+
# try cache
|
55
|
+
json = @redis.get(key)
|
56
|
+
return JSON.parse(json) if json
|
57
|
+
|
58
|
+
# get base object from base store
|
59
|
+
bse = @base.read(urg)
|
60
|
+
return nil if !bse
|
61
|
+
|
62
|
+
# assemble
|
63
|
+
seen = Set.new.add(urg)
|
64
|
+
ary = []
|
65
|
+
roles = Set.new
|
66
|
+
grps = Set.new
|
67
|
+
perms = Set.new
|
68
|
+
if bse['roles']
|
69
|
+
ary.concat bse['roles']
|
70
|
+
roles.merge bse['roles']
|
71
|
+
end
|
72
|
+
if bse['groups']
|
73
|
+
ary.concat bse['groups']
|
74
|
+
grps.merge bse['groups']
|
75
|
+
end
|
76
|
+
if bse['perms']
|
77
|
+
perms.merge bse['perms']
|
78
|
+
end
|
79
|
+
|
80
|
+
# call ourself recursively for any un-expanded memberships
|
81
|
+
while itm = ary.shift
|
82
|
+
next if seen.include?(itm)
|
83
|
+
seen.add(itm)
|
84
|
+
ikey = _key(itm)
|
85
|
+
|
86
|
+
# all included u/r/g have been seen & expanded
|
87
|
+
mem = self.read(itm)
|
88
|
+
next if !mem
|
89
|
+
if mem['roles']
|
90
|
+
roles.merge mem['roles']
|
91
|
+
seen.merge mem['roles']
|
92
|
+
end
|
93
|
+
if mem['groups']
|
94
|
+
grps.merge mem['groups']
|
95
|
+
seen.merge mem['groups']
|
96
|
+
end
|
97
|
+
if mem['perms']
|
98
|
+
perms.merge mem['perms']
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# final result
|
103
|
+
bse['roles'] = roles.to_a unless roles.empty?
|
104
|
+
bse['groups'] = grps.to_a unless grps.empty?
|
105
|
+
bse['perms'] = perms.to_a unless perms.empty?
|
106
|
+
json = JSON.pretty_generate(bse)
|
107
|
+
|
108
|
+
# save to cache
|
109
|
+
@redis.set(key, json)
|
110
|
+
@redis.expire(key, @exp)
|
111
|
+
return bse
|
112
|
+
end # def read()
|
113
|
+
|
114
|
+
|
115
|
+
###############################################
|
116
|
+
# (see Users#write)
|
117
|
+
#
|
118
|
+
def write(obj)
|
119
|
+
json = Items.generate(obj, 'User/Role/Group'.freeze, Users::ValUser)
|
120
|
+
key = _key(obj['name'])
|
121
|
+
@redis.del(key)
|
122
|
+
@base.write(obj)
|
123
|
+
end # def write()
|
124
|
+
|
125
|
+
end # class ICFS::UsersRedis
|
126
|
+
|
127
|
+
end # module ICFS
|
@@ -0,0 +1,68 @@
|
|
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 'aws-sdk-s3'
|
13
|
+
|
14
|
+
module ICFS
|
15
|
+
|
16
|
+
##########################################################################
|
17
|
+
# Implements {ICFS::Users Users} from AWS S3
|
18
|
+
#
|
19
|
+
class UsersS3 < Users
|
20
|
+
|
21
|
+
###############################################
|
22
|
+
# New instance
|
23
|
+
#
|
24
|
+
# @param s3 [Aws::S3::Client] the configured S3 client
|
25
|
+
# @param bucket [String] The bucket name
|
26
|
+
# @param prefix [String] Prefix to use for object keys
|
27
|
+
#
|
28
|
+
def initialize(s3, bucket, prefix=nil)
|
29
|
+
@s3 = s3
|
30
|
+
@bck = bucket
|
31
|
+
@pre = prefix || ''.freeze
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
###############################################
|
36
|
+
# Where to store onfig
|
37
|
+
#
|
38
|
+
def _path(user)
|
39
|
+
@pre + user
|
40
|
+
end # def _path()
|
41
|
+
private :_path
|
42
|
+
|
43
|
+
|
44
|
+
###############################################
|
45
|
+
# (see Users#read)
|
46
|
+
#
|
47
|
+
def read(urg)
|
48
|
+
Items.validate(urg, 'User/Role/Group name'.freeze, Items::FieldUsergrp)
|
49
|
+
json = @s3.get_object( bucket: @bck, key: _path(urg) ).body.read
|
50
|
+
return JSON.parse(json)
|
51
|
+
rescue
|
52
|
+
return nil
|
53
|
+
end # def read()
|
54
|
+
|
55
|
+
|
56
|
+
###############################################
|
57
|
+
# (see Users#write)
|
58
|
+
#
|
59
|
+
def write(obj)
|
60
|
+
Items.validate(obj, 'User/Role/Group'.freeze, Users::ValUser)
|
61
|
+
json = JSON.pretty_generate(obj)
|
62
|
+
@s3.put_object( bucket: @bck, key: _path(obj['name']), body: json )
|
63
|
+
end # def write()
|
64
|
+
|
65
|
+
|
66
|
+
end # class ICFS::UsersS3
|
67
|
+
|
68
|
+
end # module ICFS
|
data/lib/icfs/utils/backup.rb
CHANGED
@@ -10,6 +10,7 @@
|
|
10
10
|
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
11
11
|
|
12
12
|
require_relative '../store_fs'
|
13
|
+
|
13
14
|
#
|
14
15
|
module ICFS
|
15
16
|
|
@@ -20,6 +21,7 @@ module Utils
|
|
20
21
|
#
|
21
22
|
class Backup
|
22
23
|
|
24
|
+
|
23
25
|
###############################################
|
24
26
|
# Instance
|
25
27
|
#
|
@@ -37,7 +39,7 @@ class Backup
|
|
37
39
|
###############################################
|
38
40
|
# Copy an item
|
39
41
|
#
|
40
|
-
def
|
42
|
+
def _copy_item(dest, title, read, write, args, val=nil)
|
41
43
|
@log.debug('ICFS copy: %s'.freeze % title)
|
42
44
|
|
43
45
|
# read the item
|
@@ -65,7 +67,7 @@ class Backup
|
|
65
67
|
@log.error('ICFS copy: %s bad JSON'.freeze % title)
|
66
68
|
return nil
|
67
69
|
end # def _item
|
68
|
-
private :
|
70
|
+
private :_copy_item
|
69
71
|
|
70
72
|
|
71
73
|
###############################################
|
@@ -76,8 +78,16 @@ class Backup
|
|
76
78
|
# @param lnum_max [Integer] The highest log
|
77
79
|
# @param lnum_min [Integer] The lowest log
|
78
80
|
#
|
79
|
-
def copy(cid, dest,
|
81
|
+
def copy(cid, dest, lnum_min=1, lnum_max=0)
|
80
82
|
@log.info('ICFS copy: %s %d-%d'.freeze % [cid, lnum_min, lnum_max])
|
83
|
+
|
84
|
+
# if no max specified, pull from current
|
85
|
+
if lnum_max == 0
|
86
|
+
json = @cache.current_read(cid)
|
87
|
+
cur = Items.parse(json, 'current'.freeze, Items::ItemCurrent)
|
88
|
+
lnum_max = cur['log']
|
89
|
+
end
|
90
|
+
|
81
91
|
if lnum_min > lnum_max
|
82
92
|
raise ArgumentError, 'ICFS copy, log num min is larger than max'.freeze
|
83
93
|
end
|
@@ -105,7 +115,7 @@ class Backup
|
|
105
115
|
'entry %d-%d'.freeze % [enum, lnum],
|
106
116
|
:entry_read,
|
107
117
|
:entry_write,
|
108
|
-
[cid, lnum]
|
118
|
+
[cid, enum, lnum]
|
109
119
|
)
|
110
120
|
|
111
121
|
# index
|
@@ -121,7 +131,7 @@ class Backup
|
|
121
131
|
|
122
132
|
# action
|
123
133
|
if log['action']
|
124
|
-
anum = log['action']['
|
134
|
+
anum = log['action']['num']
|
125
135
|
_copy_item(dest,
|
126
136
|
'action %d-%d'.freeze % [anum, lnum],
|
127
137
|
:action_read,
|
@@ -133,7 +143,7 @@ class Backup
|
|
133
143
|
# case
|
134
144
|
if log['case_hash']
|
135
145
|
_copy_item(dest,
|
136
|
-
'case %d'.freeze
|
146
|
+
'case %d'.freeze % lnum,
|
137
147
|
:case_read,
|
138
148
|
:case_write,
|
139
149
|
[cid, lnum]
|
@@ -142,7 +152,8 @@ class Backup
|
|
142
152
|
|
143
153
|
# files
|
144
154
|
if log['files_hash']
|
145
|
-
log['files_hash'].each_index do |
|
155
|
+
log['files_hash'].each_index do |fraw|
|
156
|
+
fnum = fraw + 1
|
146
157
|
|
147
158
|
@log.debug('ICFS copy: file %d-%d-%d'.freeze % [enum, lnum, fnum])
|
148
159
|
|
@@ -170,6 +181,200 @@ class Backup
|
|
170
181
|
end # def copy()
|
171
182
|
|
172
183
|
|
184
|
+
###############################################
|
185
|
+
# Restore an item
|
186
|
+
#
|
187
|
+
def _restore_item(src, title, read, write, args_st, args_ca, val=nil)
|
188
|
+
@log.debug('ICFS restore: %s'.freeze % title)
|
189
|
+
|
190
|
+
# read the item
|
191
|
+
json = src.send(read, *args_st)
|
192
|
+
if !json
|
193
|
+
@log.warn('ICFS restore: %s is missing'.freeze % title)
|
194
|
+
return nil
|
195
|
+
end
|
196
|
+
|
197
|
+
# parse item if requested
|
198
|
+
if val
|
199
|
+
obj = JSON.parse(json)
|
200
|
+
err = Validate.check(obj, val)
|
201
|
+
if err
|
202
|
+
@log.error('ICFS restore: %s bad format'.freeze % title)
|
203
|
+
return nil
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
# write the item to store & cache
|
208
|
+
@store.send(write, *args_st, json)
|
209
|
+
@cache.send(write, *args_ca, json)
|
210
|
+
return [obj, json]
|
211
|
+
|
212
|
+
end # def _restore_item()
|
213
|
+
private :_restore_item
|
214
|
+
|
215
|
+
|
216
|
+
###############################################
|
217
|
+
# Restore a backup into a case
|
218
|
+
#
|
219
|
+
# @param cid [String] The case ID
|
220
|
+
# @param src [Store] Source store
|
221
|
+
# @param lnum_max [Integer] The highest log
|
222
|
+
# @param lnum_min [Integer] The lowest log
|
223
|
+
#
|
224
|
+
def restore(cid, src, lnum_min=0, lnum_max=0)
|
225
|
+
@log.info('ICFS restore: %s %d-%d'.freeze % [cid, lnum_min, lnum_max])
|
226
|
+
|
227
|
+
# take lock
|
228
|
+
@cache.lock_take(cid)
|
229
|
+
begin
|
230
|
+
|
231
|
+
# read current
|
232
|
+
json = @cache.current_read(cid)
|
233
|
+
if json
|
234
|
+
cur = Items.parse(json, 'current'.freeze, Items::ItemCurrent)
|
235
|
+
else
|
236
|
+
cur = {
|
237
|
+
'icfs' => 1,
|
238
|
+
'caseid' => cid,
|
239
|
+
'log' => 1,
|
240
|
+
'entry' => 1,
|
241
|
+
'action' => 0,
|
242
|
+
'index' => 0
|
243
|
+
}
|
244
|
+
end
|
245
|
+
|
246
|
+
# if no min specified, pull from current or default to 1
|
247
|
+
lnum_min = cur['log'] if lnum_min == 0
|
248
|
+
|
249
|
+
# sanity check min & max
|
250
|
+
if (lnum_min > lnum_max) && (lnum_max != 0)
|
251
|
+
raise ArgumentError: 'ICFS restore, log min is larger than max'.freeze
|
252
|
+
end
|
253
|
+
|
254
|
+
# max entry, action, index
|
255
|
+
emax = cur['entry']
|
256
|
+
amax = cur['action']
|
257
|
+
imax = cur['index']
|
258
|
+
|
259
|
+
# each log
|
260
|
+
lnum = lnum_min
|
261
|
+
llast = nil
|
262
|
+
while lnum != lnum_max
|
263
|
+
|
264
|
+
# copy the log
|
265
|
+
log, litem = _restore_item(src,
|
266
|
+
'log %d'.freeze % lnum,
|
267
|
+
:log_read,
|
268
|
+
:log_write,
|
269
|
+
[cid, lnum],
|
270
|
+
[cid, lnum],
|
271
|
+
Items::ItemLog
|
272
|
+
)
|
273
|
+
|
274
|
+
# no log - all done
|
275
|
+
if !log
|
276
|
+
break
|
277
|
+
else
|
278
|
+
llast = litem
|
279
|
+
end
|
280
|
+
|
281
|
+
# entry
|
282
|
+
enum = log['entry']['num']
|
283
|
+
_restore_item(src,
|
284
|
+
'entry %d-%d'.freeze % [enum, lnum],
|
285
|
+
:entry_read,
|
286
|
+
:entry_write,
|
287
|
+
[cid, enum, lnum],
|
288
|
+
[cid, enum]
|
289
|
+
)
|
290
|
+
emax = enum if enum > emax
|
291
|
+
|
292
|
+
# index
|
293
|
+
if log['index']
|
294
|
+
xnum = log['index']['num']
|
295
|
+
_restore_item(src,
|
296
|
+
'index %d-%d'.freeze % [xnum, lnum],
|
297
|
+
:index_read,
|
298
|
+
:index_write,
|
299
|
+
[cid, xnum, lnum],
|
300
|
+
[cid, xnum]
|
301
|
+
)
|
302
|
+
imax = xnum if xnum > imax
|
303
|
+
end
|
304
|
+
|
305
|
+
# action
|
306
|
+
if log['action']
|
307
|
+
anum = log['action']['num']
|
308
|
+
_restore_item(src,
|
309
|
+
'action %d-%d'.freeze % [anum, lnum],
|
310
|
+
:action_read,
|
311
|
+
:action_write,
|
312
|
+
[cid, anum, lnum],
|
313
|
+
[cid, anum]
|
314
|
+
)
|
315
|
+
amax = anum if anum > amax
|
316
|
+
end
|
317
|
+
|
318
|
+
# case
|
319
|
+
if log['case_hash']
|
320
|
+
_restore_item(src,
|
321
|
+
'case %d'.freeze % lnum,
|
322
|
+
:case_read,
|
323
|
+
:case_write,
|
324
|
+
[cid, lnum],
|
325
|
+
[cid]
|
326
|
+
)
|
327
|
+
end
|
328
|
+
|
329
|
+
# files
|
330
|
+
if log['files_hash']
|
331
|
+
log['files_hash'].each_index do |fraw|
|
332
|
+
fnum = fraw + 1
|
333
|
+
|
334
|
+
@log.debug('ICFS restore: file %d-%d-%d'.freeze % [enum, lnum, fnum])
|
335
|
+
|
336
|
+
# read
|
337
|
+
fi = src.file_read(cid, enum, lnum, fnum)
|
338
|
+
if !fi
|
339
|
+
@log.warn('ICFS restore: file %d-%d-%d missing'.freeze %
|
340
|
+
[enum, lnum, fnum])
|
341
|
+
next
|
342
|
+
end
|
343
|
+
|
344
|
+
# copy
|
345
|
+
tmp = @store.tempfile
|
346
|
+
IO.copy_stream(fi, tmp)
|
347
|
+
src.close(fi)
|
348
|
+
|
349
|
+
# write
|
350
|
+
@store.file_write(cid, enum, lnum, fnum, tmp)
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
lnum += 1
|
355
|
+
end
|
356
|
+
|
357
|
+
# write current
|
358
|
+
cur = {
|
359
|
+
'icfs' => 1,
|
360
|
+
'caseid' => cid,
|
361
|
+
'log' => lnum-1,
|
362
|
+
'entry' => emax,
|
363
|
+
'action' => amax,
|
364
|
+
'index' => imax,
|
365
|
+
'hash' => ICFS.hash(llast)
|
366
|
+
}
|
367
|
+
nitem = Items.generate(cur, 'current'.freeze, Items::ItemCurrent)
|
368
|
+
@cache.current_write(cid, nitem)
|
369
|
+
|
370
|
+
ensure
|
371
|
+
# release lock
|
372
|
+
@cache.lock_release(cid)
|
373
|
+
end
|
374
|
+
|
375
|
+
end # def restore()
|
376
|
+
|
377
|
+
|
173
378
|
end # class ICFS::Utils::Backup
|
174
379
|
|
175
380
|
end # module ICFS::Utils
|
data/lib/icfs/utils/check.rb
CHANGED
@@ -16,16 +16,12 @@ module ICFS
|
|
16
16
|
##########################################################################
|
17
17
|
# Low level utilities for working directly with ICFS systems.
|
18
18
|
#
|
19
|
-
# @todo Archive/move case tool written
|
20
|
-
#
|
21
19
|
module Utils
|
22
20
|
|
23
21
|
|
24
22
|
##########################################################################
|
25
23
|
# Check a case for errors
|
26
24
|
#
|
27
|
-
# @todo Make a function to auto find the last log
|
28
|
-
#
|
29
25
|
class Check
|
30
26
|
|
31
27
|
|
@@ -180,7 +176,7 @@ class Check
|
|
180
176
|
['index'.freeze, 1].freeze,
|
181
177
|
['log'.freeze, 2].freeze
|
182
178
|
]
|
183
|
-
)
|
179
|
+
)
|
184
180
|
idx_cur.add(xnum)
|
185
181
|
end
|
186
182
|
|