qdumpfs 1.9.1 → 1.10.0

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 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