qdumpfs 1.9.1 → 1.10.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fc77dcb0cc06a9f0bd3c05042fa61088e5babf3d5a70fdfa4b1788a6ae4e076e
4
- data.tar.gz: e4923e91396f39f7311d8ceb3c1f1128bbbc606a76c55fd84a0753f10341505e
3
+ metadata.gz: 4ace57748311474078255f93323c921f04c1d63ce40f04bb12ec15d0e48a38e8
4
+ data.tar.gz: 7c214b8b293a6f5feee3573d3d87d09be7fb18f43d1db6376b2c521081025bd5
5
5
  SHA512:
6
- metadata.gz: 311b46a74804b78ba6627130a7e2e400dfc0a3e16bfdcd197f6b9406d3f90802117999a5d6d851881b4664e78e213d117ef07a0842757d629b349ad55f271883
7
- data.tar.gz: 05c44d7d33a0649cf63a751ec5e12fe0e64030678e52a2774b6d6e4802ce6e8bee12f9742c03d3c16fd6ec26a9fad34ce420ec1a55103fe930b9d0e8e8038ea9
6
+ metadata.gz: 10107064217f62fb81170d3d6a01d8de7e18ec2953aed30c3d6fe0d68d0f0a8b157745b7277b9b92e8a61aa6bf49d7c8f9b42144cc3724f6279d7719caf316ee
7
+ data.tar.gz: d82bc50988880296280b4cf7c6fd47669288ffefa72049829ea9e3219ea2ab2769e33b6dda41f8f847c620f5d1b6e053f9aeb47bb2bbe07b105d21f12945d3a0
data/.idea/qdumpfs.iml CHANGED
@@ -10,50 +10,82 @@
10
10
  <sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
11
11
  <excludeFolder url="file://$MODULE_DIR$/.idea/copilot/chatSessions" />
12
12
  </content>
13
- <orderEntry type="inheritedJdk" />
13
+ <orderEntry type="jdk" jdkName="rbenv: 3.1.6" jdkType="RUBY_SDK" />
14
14
  <orderEntry type="sourceFolder" forTests="false" />
15
15
  <orderEntry type="module-library">
16
- <library name="minitest (v5.22.3) [path][gem]" type="rubylib">
16
+ <library name="minitest (v5.25.1) [path][gem]" type="rubylib">
17
17
  <properties>
18
- <option name="version" value="4" />
18
+ <option name="additionalInfo">
19
+ <AdditionalInfo>
20
+ <option name="authors" value="該当なし" />
21
+ <option name="email" value="該当なし" />
22
+ <option name="homepage" value="該当なし" />
23
+ <option name="summary" value="該当なし" />
24
+ </AdditionalInfo>
25
+ </option>
26
+ <option name="fromPath" value="true" />
27
+ <option name="name" value="minitest" />
28
+ <option name="requirePaths">
29
+ <list>
30
+ <option value="lib" />
31
+ </list>
32
+ </option>
33
+ <option name="url" value="file://$MODULE_DIR$/vendor/bundle/ruby/3.1.0/gems/minitest-5.25.1" />
34
+ <option name="version" value="5.25.1" />
19
35
  </properties>
20
36
  <CLASSES>
21
- <root url="file://$MODULE_DIR$/vendor/bundle/ruby/3.1.0/gems/minitest-5.22.3/lib" />
22
- <root url="file://$MODULE_DIR$/vendor/bundle/ruby/3.1.0/gems/minitest-5.22.3/test" />
37
+ <root url="file://$MODULE_DIR$/vendor/bundle/ruby/3.1.0/gems/minitest-5.25.1/lib" />
38
+ <root url="file://$MODULE_DIR$/vendor/bundle/ruby/3.1.0/gems/minitest-5.25.1/test" />
23
39
  </CLASSES>
24
40
  <JAVADOC />
25
41
  <SOURCES>
26
- <root url="file://$MODULE_DIR$/vendor/bundle/ruby/3.1.0/gems/minitest-5.22.3/lib" />
27
- <root url="file://$MODULE_DIR$/vendor/bundle/ruby/3.1.0/gems/minitest-5.22.3/test" />
42
+ <root url="file://$MODULE_DIR$/vendor/bundle/ruby/3.1.0/gems/minitest-5.25.1/lib" />
43
+ <root url="file://$MODULE_DIR$/vendor/bundle/ruby/3.1.0/gems/minitest-5.25.1/test" />
28
44
  </SOURCES>
29
45
  <excluded>
30
- <root url="file://$MODULE_DIR$/vendor/bundle/ruby/3.1.0/gems/minitest-5.22.3/test" />
46
+ <root url="file://$MODULE_DIR$/vendor/bundle/ruby/3.1.0/gems/minitest-5.25.1/test" />
31
47
  </excluded>
32
48
  </library>
33
49
  </orderEntry>
34
50
  <orderEntry type="module-library">
35
- <library name="rake (v13.2.0) [path][gem]" type="rubylib">
51
+ <library name="rake (v13.2.1) [path][gem]" type="rubylib">
36
52
  <properties>
37
- <option name="version" value="4" />
53
+ <option name="additionalInfo">
54
+ <AdditionalInfo>
55
+ <option name="authors" value="該当なし" />
56
+ <option name="email" value="該当なし" />
57
+ <option name="homepage" value="該当なし" />
58
+ <option name="summary" value="該当なし" />
59
+ </AdditionalInfo>
60
+ </option>
61
+ <option name="fromPath" value="true" />
62
+ <option name="name" value="rake" />
63
+ <option name="requirePaths">
64
+ <list>
65
+ <option value="lib" />
66
+ </list>
67
+ </option>
68
+ <option name="url" value="file://$MODULE_DIR$/vendor/bundle/ruby/3.1.0/gems/rake-13.2.1" />
69
+ <option name="version" value="13.2.1" />
38
70
  </properties>
39
71
  <CLASSES>
40
- <root url="file://$MODULE_DIR$/vendor/bundle/ruby/3.1.0/gems/rake-13.2.0/doc" />
41
- <root url="file://$MODULE_DIR$/vendor/bundle/ruby/3.1.0/gems/rake-13.2.0/exe" />
42
- <root url="file://$MODULE_DIR$/vendor/bundle/ruby/3.1.0/gems/rake-13.2.0/lib" />
72
+ <root url="file://$MODULE_DIR$/vendor/bundle/ruby/3.1.0/gems/rake-13.2.1/doc" />
73
+ <root url="file://$MODULE_DIR$/vendor/bundle/ruby/3.1.0/gems/rake-13.2.1/exe" />
74
+ <root url="file://$MODULE_DIR$/vendor/bundle/ruby/3.1.0/gems/rake-13.2.1/lib" />
43
75
  </CLASSES>
44
76
  <JAVADOC />
45
77
  <SOURCES>
46
- <root url="file://$MODULE_DIR$/vendor/bundle/ruby/3.1.0/gems/rake-13.2.0/doc" />
47
- <root url="file://$MODULE_DIR$/vendor/bundle/ruby/3.1.0/gems/rake-13.2.0/exe" />
48
- <root url="file://$MODULE_DIR$/vendor/bundle/ruby/3.1.0/gems/rake-13.2.0/lib" />
78
+ <root url="file://$MODULE_DIR$/vendor/bundle/ruby/3.1.0/gems/rake-13.2.1/doc" />
79
+ <root url="file://$MODULE_DIR$/vendor/bundle/ruby/3.1.0/gems/rake-13.2.1/exe" />
80
+ <root url="file://$MODULE_DIR$/vendor/bundle/ruby/3.1.0/gems/rake-13.2.1/lib" />
49
81
  </SOURCES>
50
82
  <excluded>
51
- <root url="file://$MODULE_DIR$/vendor/bundle/ruby/3.1.0/gems/rake-13.2.0/doc" />
52
- <root url="file://$MODULE_DIR$/vendor/bundle/ruby/3.1.0/gems/rake-13.2.0/exe" />
83
+ <root url="file://$MODULE_DIR$/vendor/bundle/ruby/3.1.0/gems/rake-13.2.1/doc" />
84
+ <root url="file://$MODULE_DIR$/vendor/bundle/ruby/3.1.0/gems/rake-13.2.1/exe" />
53
85
  </excluded>
54
86
  </library>
55
87
  </orderEntry>
56
- <orderEntry type="library" scope="PROVIDED" name="bundler (v2.4.20, rbenv: 3.1.3) [gem]" level="application" />
88
+ <orderEntry type="library" scope="PROVIDED" name="bundler (v2.3.27, rbenv: 3.1.6) [gem]" level="application" />
57
89
  </component>
58
90
  <component name="RakeTasksCache-v2">
59
91
  <option name="myRootTask">
data/Gemfile.lock CHANGED
@@ -1,12 +1,12 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- qdumpfs (1.9.1)
4
+ qdumpfs (1.10.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
8
8
  specs:
9
- minitest (5.25.1)
9
+ minitest (5.25.2)
10
10
  rake (13.2.1)
11
11
 
12
12
  PLATFORMS
@@ -59,11 +59,7 @@ module Qdumpfs
59
59
  @file.puts msg
60
60
  end
61
61
  end
62
-
63
-
64
- class Error < StandardError
65
- end
66
-
62
+
67
63
 
68
64
  class NullMatcher
69
65
  def initialize(options = {})
@@ -1,4 +1,4 @@
1
1
  module Qdumpfs
2
- VERSION = "1.9.1"
2
+ VERSION = "1.10.0"
3
3
  end
4
4
 
data/lib/qdumpfs.rb CHANGED
@@ -198,6 +198,8 @@ module Qdumpfs
198
198
  # ファイルのアップデート
199
199
  update_file(s, l, t)
200
200
  dirs[t] = File.stat(s) if File.ftype(s) == "directory"
201
+ rescue Errno::ENOSPC
202
+ raise
201
203
  rescue => e
202
204
  report_error(s, e)
203
205
  @error_files << [s, e.message]
@@ -235,6 +237,8 @@ module Qdumpfs
235
237
  end
236
238
  chown_if_root(type, s, t)
237
239
  dirs[t] = File.stat(s) if File.ftype(s) == "directory"
240
+ rescue Errno::ENOSPC
241
+ raise
238
242
  rescue => e
239
243
  report_error(s, e)
240
244
  @error_files << [s, e.message]
@@ -309,7 +313,29 @@ module Qdumpfs
309
313
  }
310
314
  return nil
311
315
  end
312
-
316
+
317
+ def incomplete_backup(today)
318
+ # puts "incomplete_backup #{today}"
319
+ # バックアップが途中で終了した場合、そのディレクトリをリネームする
320
+ dirname = File.dirname(today)
321
+ prefix = "_"
322
+ incomplete = dirname.sub(/(\d+)$/, prefix + '\1')
323
+ return unless FileTest.directory?(dirname) # バックアップが途中で途中で終了。ただしまだ日付ディレクトリが存在しない場合はなにもしない。
324
+
325
+ # すでにincompleteが存在する場合はユニークな名前を探す
326
+ 0.upto(10) do |i|
327
+ break unless FileTest.exist?(incomplete)
328
+ prefix += "_"
329
+ incomplete = dirname.sub(/(\d+)$/, prefix + '\1')
330
+ end
331
+ if FileTest.directory?(incomplete)
332
+ log("cannot rename incomplete backup: #{incomplete}")
333
+ else
334
+ FileUtils.mv(dirname, incomplete)
335
+ log("rename incomplete backup: #{dirname} -> #{incomplete}")
336
+ end
337
+ end
338
+
313
339
  def backup
314
340
  ##### オリジナルのバックアップルーチン
315
341
  @opt.validate_directories(2)
@@ -336,31 +362,37 @@ module Qdumpfs
336
362
  base = File.basename(src)
337
363
  dirname = File.dirname(src)
338
364
  raise RuntimeError unless FileTest.exist?(dirname + '/' + base)
339
-
340
- # 存在するバックアップの最新を取得
341
- latest = latest_snapshot(start_time, src, dst, base)
342
- # 現在の日付フォルダを取得j:/to/backup1/2019/05/10/home
343
- today = File.join(dst, datedir(start_time), base)
344
- File.umask(0077)
345
- FileUtils.mkpath(today) unless @opt.dry_run
346
- if windows?
347
- src = src.sub( /^[A-Za-z]:$/, src + "/" )
348
- end
349
- if latest
350
- # バックアップがすでに存在する場合差分コピー
351
- log("## update_snapshot #{src} #{latest}=>#{today} ##")
352
- update_snapshot(src, latest, today)
353
- else
354
- # 初回は単純に再帰コピー
355
- log("## recursive_copy #{src}=>#{today} ##")
356
- recursive_copy(src, today)
357
- end
358
- unless @opt.dry_run
359
- create_latest_symlink(dst, today)
360
- elapsed = Time.now - start_time
361
- log_result(src, today, elapsed)
365
+
366
+ begin
367
+ # 存在するバックアップの最新を取得
368
+ latest = latest_snapshot(start_time, src, dst, base)
369
+ # 現在の日付フォルダを取得j:/to/backup1/2019/05/10/home
370
+ today = File.join(dst, datedir(start_time), base)
371
+ File.umask(0077)
372
+ FileUtils.mkpath(today) unless @opt.dry_run
373
+ if windows?
374
+ src = src.sub( /^[A-Za-z]:$/, src + "/" )
375
+ end
376
+ if latest
377
+ # バックアップがすでに存在する場合差分コピー
378
+ log("## update_snapshot #{src} #{latest}=>#{today} ##")
379
+ update_snapshot(src, latest, today)
380
+ else
381
+ # 初回は単純に再帰コピー
382
+ log("## recursive_copy #{src}=>#{today} ##")
383
+ recursive_copy(src, today)
384
+ end
385
+ unless @opt.dry_run
386
+ create_latest_symlink(dst, today)
387
+ elapsed = Time.now - start_time
388
+ log_result(src, today, elapsed)
389
+ end
390
+ log("##### backup end #####")
391
+ rescue Errno::ENOSPC
392
+ # 直前のmkpathでエラーが発生した場合 Errno::ENOSPCが発生する。内部ではIncompleteErrorに変換している
393
+ incomplete_backup(today)
394
+ log("##### backup incomplete(No space left on device) #####")
362
395
  end
363
- log("##### backup end #####")
364
396
  end
365
397
 
366
398
  def sync
@@ -379,49 +411,55 @@ module Qdumpfs
379
411
  log("##### sync start #{fmt(start_time)} => limit_time=#{fmt(limit_time)} #####")
380
412
  count = 0
381
413
  last_sync_complete = false
382
- while true
383
- count += 1
384
- log("## sync_latest count=#{count} ##")
385
- latest_start = Time.now
386
- sync_result, from, to = sync_latest(src, dst)
387
- latest_end = Time.now
388
-
389
- log("## sync_latest result=#{sync_result} from=#{from} to=#{to} ##")
390
- unless sync_result
391
- # 同期結果がtrueでない場合ここで終了。ただしsync_result=falseになるのはコピー元フォルダが存在しない場合なので、
392
- # 中途半端な結果にはならない
393
- last_sync_complete = true
394
- break
395
- end
396
-
397
- from_count, to_count = do_verify(from, to)
398
- log("## from_count=#{from_count} to_count=#{to_count} equals=#{from_count == to_count} ##")
399
- unless from_count == to_count
400
- # ファイル数が同じでない場合ここで終了
401
- last_sync_complete = false
402
- break
403
- end
404
-
405
- # 次回同期にかかる時間を最終同期時間の半分と予想
406
- next_sync = (latest_end - latest_start) / 2
407
-
408
- cur_time = Time.now
409
- in_limit = (cur_time + next_sync) < limit_time
410
- log("## cur_time=#{fmt(cur_time)} + next_sync=#{next_sync} < limit_time=#{fmt(limit_time)} in_limit=#{in_limit} ## ")
411
- unless in_limit
412
- # 指定時間内ではない場合ここで終了(ただし最終同期は成功)
413
- last_sync_complete = true
414
- break
414
+
415
+ begin
416
+ while true
417
+ count += 1
418
+ log("## sync_latest count=#{count} ##")
419
+ latest_start = Time.now
420
+ sync_result, from, to = sync_latest(src, dst)
421
+ latest_end = Time.now
422
+
423
+ log("## sync_latest result=#{sync_result} from=#{from} to=#{to} ##")
424
+ unless sync_result
425
+ # 同期結果がtrueでない場合ここで終了。ただしsync_result=falseになるのはコピー元フォルダが存在しない場合なので、
426
+ # 中途半端な結果にはならない
427
+ last_sync_complete = true
428
+ break
429
+ end
430
+
431
+ from_count, to_count = do_verify(from, to)
432
+ log("## from_count=#{from_count} to_count=#{to_count} equals=#{from_count == to_count} ##")
433
+ unless from_count == to_count
434
+ # ファイル数が同じでない場合ここで終了
435
+ last_sync_complete = false
436
+ break
437
+ end
438
+
439
+ # 次回同期にかかる時間を最終同期時間の半分と予想
440
+ next_sync = (latest_end - latest_start) / 2
441
+
442
+ cur_time = Time.now
443
+ in_limit = (cur_time + next_sync) < limit_time
444
+ log("## cur_time=#{fmt(cur_time)} + next_sync=#{next_sync} < limit_time=#{fmt(limit_time)} in_limit=#{in_limit} ## ")
445
+ unless in_limit
446
+ # 指定時間内ではない場合ここで終了(ただし最終同期は成功)
447
+ last_sync_complete = true
448
+ break
449
+ end
415
450
  end
416
- end
417
-
418
- end_time = Time.now
419
- diff = time_diff(start_time, end_time)
420
451
 
421
- elapsed = Time.now - start_time
422
- log_result(src, dst, elapsed)
423
-
424
- log("##### sync end #{fmt(end_time)} diff=#{diff} last_sync_complete=#{last_sync_complete} #####")
452
+ end_time = Time.now
453
+ diff = time_diff(start_time, end_time)
454
+
455
+ elapsed = Time.now - start_time
456
+ log_result(src, dst, elapsed)
457
+
458
+ log("##### sync end #{fmt(end_time)} diff=#{diff} last_sync_complete=#{last_sync_complete} #####")
459
+ rescue Errno::ENOSPC
460
+ incomplete_backup(today)
461
+ log("##### backup incomplete(No space left on device) #####")
462
+ end
425
463
  end
426
464
 
427
465
  def verify
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: qdumpfs
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.9.1
4
+ version: 1.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - src
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-11-12 00:00:00.000000000 Z
11
+ date: 2024-12-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler