icfs 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
|