Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitbc0844a

Browse files
committed
enhance: config restore with mount point handling#1419
1 parent8396599 commitbc0844a

File tree

5 files changed

+473
-8
lines changed

5 files changed

+473
-8
lines changed

‎internal/backup/restore.go‎

Lines changed: 118 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"os"
88
"path/filepath"
99
"strings"
10+
"syscall"
1011

1112
"github.com/0xJacky/Nginx-UI/internal/nginx"
1213
"github.com/0xJacky/Nginx-UI/settings"
@@ -345,9 +346,9 @@ func verifyHashes(restoreDir, nginxUIZipPath, nginxZipPath string) (bool, error)
345346
// parseHashInfo parses hash info from content string
346347
funcparseHashInfo(contentstring)HashInfo {
347348
info:=HashInfo{}
348-
lines:=strings.Split(content,"\n")
349+
lines:=strings.SplitSeq(content,"\n")
349350

350-
for_,line:=rangelines {
351+
forline:=rangelines {
351352
line=strings.TrimSpace(line)
352353
ifline=="" {
353354
continue
@@ -383,35 +384,146 @@ func restoreNginxConfigs(nginxBackupDir string) error {
383384
returnErrNginxConfigDirEmpty
384385
}
385386

387+
logger.Infof("Starting Nginx config restore from %s to %s",nginxBackupDir,destDir)
388+
386389
// Recursively clean destination directory preserving the directory structure
390+
logger.Info("Cleaning destination directory before restore")
387391
iferr:=cleanDirectoryPreservingStructure(destDir);err!=nil {
392+
logger.Errorf("Failed to clean directory %s: %v",destDir,err)
388393
returncosy.WrapErrorWithParams(ErrCopyNginxConfigDir,"failed to clean directory: "+err.Error())
389394
}
390395

391396
// Copy files from backup to nginx config directory
397+
logger.Infof("Copying backup files to destination: %s",destDir)
392398
iferr:=copyDirectory(nginxBackupDir,destDir);err!=nil {
399+
logger.Errorf("Failed to copy backup files: %v",err)
393400
returnerr
394401
}
395402

403+
logger.Info("Nginx config restore completed successfully")
396404
returnnil
397405
}
398406

399-
// cleanDirectoryPreservingStructure removes all files andsymlinks in a directory
400-
// but preserves the directory structure itself
407+
// cleanDirectoryPreservingStructure removes all files andsubdirectories in a directory
408+
// but preserves the directory structure itself and handles mount points correctly.
401409
funccleanDirectoryPreservingStructure(dirstring)error {
410+
logger.Infof("Cleaning directory: %s",dir)
411+
402412
entries,err:=os.ReadDir(dir)
403413
iferr!=nil {
404414
returnerr
405415
}
406416

407417
for_,entry:=rangeentries {
408418
path:=filepath.Join(dir,entry.Name())
409-
err=os.RemoveAll(path)
410-
iferr!=nil {
419+
420+
iferr:=removeOrClearPath(path,entry.IsDir());err!=nil {
411421
returnerr
412422
}
413423
}
414424

425+
logger.Infof("Successfully cleaned directory: %s",dir)
426+
returnnil
427+
}
428+
429+
// removeOrClearPath removes a path or clears it if it's a mount point
430+
funcremoveOrClearPath(pathstring,isDirbool)error {
431+
// Try to remove the path first
432+
err:=os.RemoveAll(path)
433+
iferr==nil {
434+
returnnil
435+
}
436+
437+
// Handle removal failures
438+
if!isDeviceBusyError(err) {
439+
returnfmt.Errorf("failed to remove %s: %w",path,err)
440+
}
441+
442+
// Device busy - check if it's a mount point or directory
443+
if!isDir {
444+
returnfmt.Errorf("file is busy and cannot be removed: %s: %w",path,err)
445+
}
446+
447+
logger.Warnf("Path is busy (mount point): %s, clearing contents only",path)
448+
returnclearDirectoryContents(path)
449+
}
450+
451+
// isMountPoint checks if a path is a mount point by comparing device IDs
452+
// or checking /proc/mounts on Linux systems
453+
funcisMountPoint(pathstring)bool {
454+
ifisDeviceDifferent(path) {
455+
returntrue
456+
}
457+
458+
returnisInMountTable(path)
459+
}
460+
461+
// isDeviceDifferent and isInMountTable are implemented in platform-specific files:
462+
// - restore_unix.go for Linux/Unix systems
463+
// - restore_windows.go for Windows systems
464+
465+
// unescapeOctal converts octal escape sequences like \040 to their character equivalents
466+
funcunescapeOctal(sstring)string {
467+
varresult strings.Builder
468+
469+
fori:=0;i<len(s);i++ {
470+
ifchar,skip:=tryParseOctal(s,i);skip>0 {
471+
result.WriteByte(char)
472+
i+=skip-1// -1 because loop will increment
473+
continue
474+
}
475+
result.WriteByte(s[i])
476+
}
477+
478+
returnresult.String()
479+
}
480+
481+
// tryParseOctal attempts to parse octal sequence at position i
482+
// returns (char, skip) where skip > 0 if successful
483+
functryParseOctal(sstring,iint) (byte,int) {
484+
ifs[i]!='\\'||i+3>=len(s) {
485+
return0,0
486+
}
487+
488+
varcharbyte
489+
if_,err:=fmt.Sscanf(s[i:i+4],"\\%03o",&char);err==nil {
490+
returnchar,4
491+
}
492+
493+
return0,0
494+
}
495+
496+
// isDeviceBusyError checks if an error is a "device or resource busy" error
497+
funcisDeviceBusyError(errerror)bool {
498+
iferr==nil {
499+
returnfalse
500+
}
501+
502+
iferrno,ok:=err.(syscall.Errno);ok&&errno==syscall.EBUSY {
503+
returntrue
504+
}
505+
506+
errMsg:=err.Error()
507+
returnstrings.Contains(errMsg,"device or resource busy")||
508+
strings.Contains(errMsg,"resource busy")
509+
}
510+
511+
// clearDirectoryContents removes all files and subdirectories within a directory
512+
// but preserves the directory itself. This is useful for cleaning mount points.
513+
funcclearDirectoryContents(dirstring)error {
514+
entries,err:=os.ReadDir(dir)
515+
iferr!=nil {
516+
returnerr
517+
}
518+
519+
for_,entry:=rangeentries {
520+
path:=filepath.Join(dir,entry.Name())
521+
522+
iferr:=removeOrClearPath(path,entry.IsDir());err!=nil {
523+
logger.Warnf("Failed to clear %s: %v, continuing",path,err)
524+
}
525+
}
526+
415527
returnnil
416528
}
417529

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp