diff --git a/go.mod b/go.mod index e13f9a8d..c07a2d13 100644 --- a/go.mod +++ b/go.mod @@ -57,6 +57,7 @@ require ( github.com/devhaozi/huaweicloud-sdk-go-v3 v0.0.0-20241018211007-bbebb6de5db7 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/g0rbe/go-chattr v1.0.1 // indirect github.com/gabriel-vasile/mimetype v1.4.6 // indirect github.com/glebarez/go-sqlite v1.22.0 // indirect github.com/go-ole/go-ole v1.2.6 // indirect diff --git a/go.sum b/go.sum index 0603dbae..2fdbc338 100644 --- a/go.sum +++ b/go.sum @@ -21,6 +21,8 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/g0rbe/go-chattr v1.0.1 h1:CHwYB+WKB46hkzt6Jxyvkyrz7u9njghUOFvmx2gir84= +github.com/g0rbe/go-chattr v1.0.1/go.mod h1:yQc6VPJfpDDC1g+W2t47+yYmzBNioax/GLiyJ25/IOs= github.com/gabriel-vasile/mimetype v1.4.6 h1:3+PzJTKLkvgjeTbts6msPJt4DixhT4YtFNf1gtGe3zc= github.com/gabriel-vasile/mimetype v1.4.6/go.mod h1:JX1qVKqZd40hUPpAfiNTe0Sne7hdfKSbOqqmkq8GCXc= github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ= diff --git a/pkg/chattr/chattr.go b/pkg/chattr/chattr.go new file mode 100644 index 00000000..2ced3dae --- /dev/null +++ b/pkg/chattr/chattr.go @@ -0,0 +1,123 @@ +// Package chattr https://github.com/g0rbe/go-chattr/pull/3 +package chattr + +/* +A package for change attribute of a file on Linux, similar to the chattr command. + +Example to set the immutable attribute to a file: + + file, err := os.OpenFile("file.txt", os.O_RDONLY, 0666) + + if err != nil { + panic(err) + } + + defer file.Close() + + err = chattr.SetAttr(file, chattr.FS_IMMUTABLE_FL) + + if err != nil { + panic(err) + } +*/ + +import ( + "os" + "syscall" + "unsafe" +) + +// from /usr/include/linux/fs.h +const ( + FS_SECRM_FL uint32 = 0x00000001 /* Secure deletion */ + FS_UNRM_FL = 0x00000002 /* Undelete */ + FS_COMPR_FL = 0x00000004 /* Compress file */ + FS_SYNC_FL = 0x00000008 /* Synchronous updates */ + FS_IMMUTABLE_FL = 0x00000010 /* Immutable file */ + FS_APPEND_FL = 0x00000020 /* writes to file may only append */ + FS_NODUMP_FL = 0x00000040 /* do not dump file */ + FS_NOATIME_FL = 0x00000080 /* do not update atime */ + FS_DIRTY_FL = 0x00000100 + FS_COMPRBLK_FL = 0x00000200 /* One or more compressed clusters */ + FS_NOCOMP_FL = 0x00000400 /* Don't compress */ + FS_ENCRYPT_FL = 0x00000800 /* Encrypted file */ + FS_BTREE_FL = 0x00001000 /* btree format dir */ + FS_INDEX_FL = 0x00001000 /* hash-indexed directory */ + FS_IMAGIC_FL = 0x00002000 /* AFS directory */ + FS_JOURNAL_DATA_FL = 0x00004000 /* Reserved for ext3 */ + FS_NOTAIL_FL = 0x00008000 /* file tail should not be merged */ + FS_DIRSYNC_FL = 0x00010000 /* dirsync behaviour (directories only) */ + FS_TOPDIR_FL = 0x00020000 /* Top of directory hierarchies*/ + FS_HUGE_FILE_FL = 0x00040000 /* Reserved for ext4 */ + FS_EXTENT_FL = 0x00080000 /* Extents */ + FS_EA_INODE_FL = 0x00200000 /* Inode used for large EA */ + FS_EOFBLOCKS_FL = 0x00400000 /* Reserved for ext4 */ + FS_NOCOW_FL = 0x00800000 /* Do not cow file */ + FS_INLINE_DATA_FL = 0x10000000 /* Reserved for ext4 */ + FS_PROJINHERIT_FL = 0x20000000 /* Create with parents projid */ + FS_RESERVED_FL = 0x80000000 /* reserved for ext2 lib */ + +) + +// from ioctl_list manpage +const ( + FS_IOC_GETFLAGS uintptr = 0x80086601 + FS_IOC_SETFLAGS uintptr = 0x40086602 +) + +func ioctl(f *os.File, request uintptr, attrp *uint32) error { + argp := uintptr(unsafe.Pointer(attrp)) + _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, f.Fd(), request, argp) + if errno != 0 { + return os.NewSyscallError("ioctl", errno) + } + + return nil +} + +// GetAttrs retrieves the attributes of a file. +func GetAttrs(f *os.File) (uint32, error) { + attr := uint32(1) + err := ioctl(f, FS_IOC_GETFLAGS, &attr) + + return attr, err +} + +// SetAttr sets the given attribute. +func SetAttr(f *os.File, attr uint32) error { + attrs, err := GetAttrs(f) + if err != nil { + return err + } + + attrs |= attr + + return ioctl(f, FS_IOC_SETFLAGS, &attrs) + +} + +// UnsetAttr unsets the given attribute. +func UnsetAttr(f *os.File, attr uint32) error { + attrs, err := GetAttrs(f) + if err != nil { + return err + } + + attrs ^= attrs & attr + + return ioctl(f, FS_IOC_SETFLAGS, &attrs) +} + +// IsAttr checks whether the given attribute is set. +func IsAttr(f *os.File, attr uint32) (bool, error) { + attrs, err := GetAttrs(f) + if err != nil { + return false, err + } + + if (attrs & attr) != 0 { + return true, nil + } + + return false, nil +} diff --git a/pkg/io/file.go b/pkg/io/file.go index 2f234309..00d118bb 100644 --- a/pkg/io/file.go +++ b/pkg/io/file.go @@ -4,6 +4,8 @@ import ( "os" "path/filepath" "strings" + + "github.com/TheTNB/panel/pkg/chattr" ) // Write 写入文件 @@ -12,27 +14,58 @@ func Write(path string, data string, permission os.FileMode) error { return err } - err := os.WriteFile(path, []byte(data), permission) + file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, permission) if err != nil { return err } + defer file.Close() + + iFlag, _ := chattr.IsAttr(file, chattr.FS_IMMUTABLE_FL) + aFlag, _ := chattr.IsAttr(file, chattr.FS_APPEND_FL) + if iFlag { + _ = chattr.UnsetAttr(file, chattr.FS_IMMUTABLE_FL) + } + if aFlag { + _ = chattr.UnsetAttr(file, chattr.FS_APPEND_FL) + } + + _, err = file.WriteString(data) + if err != nil { + return err + } + + if iFlag { + _ = chattr.SetAttr(file, chattr.FS_IMMUTABLE_FL) + } + if aFlag { + _ = chattr.SetAttr(file, chattr.FS_APPEND_FL) + } return nil } // WriteAppend 追加写入文件 func WriteAppend(path string, data string) error { - file, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644) if err != nil { return err } defer file.Close() + iFlag, _ := chattr.IsAttr(file, chattr.FS_IMMUTABLE_FL) + if iFlag { + _ = chattr.UnsetAttr(file, chattr.FS_IMMUTABLE_FL) + } + _, err = file.WriteString(data) if err != nil { return err } + if iFlag { + _ = chattr.SetAttr(file, chattr.FS_IMMUTABLE_FL) + } + return nil } diff --git a/pkg/io/path.go b/pkg/io/path.go index a30e2b04..ff84852d 100644 --- a/pkg/io/path.go +++ b/pkg/io/path.go @@ -12,6 +12,7 @@ import ( // Remove 删除文件/目录 func Remove(path string) error { + _, _ = shell.Execf("chattr -R -ia '%s'", path) return os.RemoveAll(path) }