add filesystem attr (on linux) support
This commit is contained in:
parent
8254fd21a3
commit
187ad868db
101
fsutils/consts.go
Normal file
101
fsutils/consts.go
Normal file
@ -0,0 +1,101 @@
|
||||
package fsutils
|
||||
|
||||
import (
|
||||
`github.com/g0rbe/go-chattr`
|
||||
)
|
||||
|
||||
// https://github.com/torvalds/linux/blob/master/include/uapi/linux/fs.h
|
||||
const (
|
||||
SecureDelete uint32 = chattr.FS_SECRM_FL // Secure deletion
|
||||
UnDelete = chattr.FS_UNRM_FL // Undelete
|
||||
CompressFile = chattr.FS_COMPR_FL // Compress file
|
||||
SyncUpdatechattr = chattr.FS_SYNC_FL // Synchronous updates
|
||||
Immutable = chattr.FS_IMMUTABLE_FL // Immutable file
|
||||
AppendOnly = chattr.FS_APPEND_FL // Writes to file may only append
|
||||
NoDumpFile = chattr.FS_NODUMP_FL // Do not dump file
|
||||
NoUpdateAtime = chattr.FS_NOATIME_FL // Do not update atime
|
||||
IsDirty = chattr.FS_DIRTY_FL // Nobody knows what this does, lol.
|
||||
CompressedClusters = chattr.FS_COMPRBLK_FL // One or more compressed clusters
|
||||
NoCompress = chattr.FS_NOCOMP_FL // Don't compress
|
||||
EncFile = chattr.FS_ENCRYPT_FL // Encrypted file
|
||||
BtreeFmt = chattr.FS_BTREE_FL // Btree format dir
|
||||
HashIdxDir = chattr.FS_INDEX_FL // Hash-indexed directory
|
||||
AfsDir = chattr.FS_IMAGIC_FL // AFS directory
|
||||
ReservedExt3 = chattr.FS_JOURNAL_DATA_FL // Reserved for ext3
|
||||
NoMergeTail = chattr.FS_NOTAIL_FL // File tail should not be merged
|
||||
DirSync = chattr.FS_DIRSYNC_FL // dirsync behaviour (directories only)
|
||||
DirTop = chattr.FS_TOPDIR_FL // Top of directory hierarchies
|
||||
ReservedExt4a = chattr.FS_HUGE_FILE_FL // Reserved for ext4
|
||||
Extents = chattr.FS_EXTENT_FL // Extents
|
||||
LargeEaInode = chattr.FS_EA_INODE_FL // Inode used for large EA
|
||||
ReservedExt4b = chattr.FS_EOFBLOCKS_FL // Reserved for ext4
|
||||
NoCOWFile = chattr.FS_NOCOW_FL // Do not cow file
|
||||
ReservedExt4c = chattr.FS_INLINE_DATA_FL // Reserved for ext4
|
||||
UseParentProjId = chattr.FS_PROJINHERIT_FL // Create with parents projid
|
||||
ReservedExt2 = chattr.FS_RESERVED_FL // Reserved for ext2 lib
|
||||
)
|
||||
|
||||
var (
|
||||
// AttrNameValueMap contains a mapping of attribute names (as designated by this package) to their flag values.
|
||||
AttrNameValueMap map[string]uint32 = map[string]uint32{
|
||||
"SecureDelete": SecureDelete,
|
||||
"UnDelete": UnDelete,
|
||||
"CompressFile": CompressFile,
|
||||
"SyncUpdatechattr": SyncUpdatechattr,
|
||||
"Immutable": Immutable,
|
||||
"AppendOnly": AppendOnly,
|
||||
"NoDumpFile": NoDumpFile,
|
||||
"NoUpdateAtime": NoUpdateAtime,
|
||||
"IsDirty": IsDirty,
|
||||
"CompressedClusters": CompressedClusters,
|
||||
"NoCompress": NoCompress,
|
||||
"EncFile": EncFile,
|
||||
"BtreeFmt": BtreeFmt,
|
||||
"HashIdxDir": HashIdxDir,
|
||||
"AfsDir": AfsDir,
|
||||
"ReservedExt3": ReservedExt3,
|
||||
"NoMergeTail": NoMergeTail,
|
||||
"DirSync": DirSync,
|
||||
"DirTop": DirTop,
|
||||
"ReservedExt4a": ReservedExt4a,
|
||||
"Extents": Extents,
|
||||
"LargeEaInode": LargeEaInode,
|
||||
"ReservedExt4b": ReservedExt4b,
|
||||
"NoCOWFile": NoCOWFile,
|
||||
"ReservedExt4c": ReservedExt4c,
|
||||
"UseParentProjId": UseParentProjId,
|
||||
"ReservedExt2": ReservedExt2,
|
||||
}
|
||||
/*
|
||||
AttrValueNameMap contains a mapping of attribute flags to their names (as designated by this package).
|
||||
Note the oddball here, BtreeFmt and HashIdxDir are actually the same value, so be forewarned.
|
||||
*/
|
||||
AttrValueNameMap map[uint32]string = map[uint32]string{
|
||||
SecureDelete: "SecureDelete",
|
||||
UnDelete: "UnDelete",
|
||||
CompressFile: "CompressFile",
|
||||
SyncUpdatechattr: "SyncUpdatechattr",
|
||||
Immutable: "Immutable",
|
||||
AppendOnly: "AppendOnly",
|
||||
NoDumpFile: "NoDumpFile",
|
||||
NoUpdateAtime: "NoUpdateAtime",
|
||||
IsDirty: "IsDirty",
|
||||
CompressedClusters: "CompressedClusters",
|
||||
NoCompress: "NoCompress",
|
||||
EncFile: "EncFile",
|
||||
BtreeFmt: "BtreeFmt|HashIdxDir", // Well THIS is silly and seems like an oversight. Both FS_BTREE_FL and FS_INDEX_FL have the same flag. Confirmed in kernel source.
|
||||
AfsDir: "AfsDir",
|
||||
ReservedExt3: "ReservedExt3",
|
||||
NoMergeTail: "NoMergeTail",
|
||||
DirSync: "DirSync",
|
||||
DirTop: "DirTop",
|
||||
ReservedExt4a: "ReservedExt4a",
|
||||
Extents: "Extents",
|
||||
LargeEaInode: "LargeEaInode",
|
||||
ReservedExt4b: "ReservedExt4b",
|
||||
NoCOWFile: "NoCOWFile",
|
||||
ReservedExt4c: "ReservedExt4c",
|
||||
UseParentProjId: "UseParentProjId",
|
||||
ReservedExt2: "ReservedExt2",
|
||||
}
|
||||
)
|
44
fsutils/funcs.go
Normal file
44
fsutils/funcs.go
Normal file
@ -0,0 +1,44 @@
|
||||
package fsutils
|
||||
|
||||
import (
|
||||
`os`
|
||||
`reflect`
|
||||
|
||||
`github.com/g0rbe/go-chattr`
|
||||
`r00t2.io/sysutils/paths`
|
||||
)
|
||||
|
||||
func GetAttrs(path string) (attrs *FsAttrs, err error) {
|
||||
|
||||
var f *os.File
|
||||
var evalAttrs FsAttrs
|
||||
var attrVal uint32
|
||||
var reflectVal reflect.Value
|
||||
var field reflect.Value
|
||||
var myPath string = path
|
||||
|
||||
if err = paths.RealPath(&myPath); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if f, err = os.Open(myPath); err != nil {
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
reflectVal = reflect.ValueOf(&evalAttrs).Elem()
|
||||
|
||||
if attrVal, err = chattr.GetAttrs(f); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for attrNm, attrInt := range AttrNameValueMap {
|
||||
field = reflectVal.FieldByName(attrNm)
|
||||
field.SetBool((attrVal & attrInt) != 0)
|
||||
}
|
||||
|
||||
attrs = new(FsAttrs)
|
||||
*attrs = evalAttrs
|
||||
|
||||
return
|
||||
}
|
43
fsutils/funcs_fsattrs.go
Normal file
43
fsutils/funcs_fsattrs.go
Normal file
@ -0,0 +1,43 @@
|
||||
package fsutils
|
||||
|
||||
import (
|
||||
`os`
|
||||
`reflect`
|
||||
|
||||
`github.com/g0rbe/go-chattr`
|
||||
`r00t2.io/sysutils/paths`
|
||||
)
|
||||
|
||||
func (f *FsAttrs) Apply(path string) (err error) {
|
||||
|
||||
var file *os.File
|
||||
var reflectVal reflect.Value
|
||||
var fieldVal reflect.Value
|
||||
|
||||
var myPath string = path
|
||||
|
||||
if err = paths.RealPath(&myPath); err != nil {
|
||||
return
|
||||
}
|
||||
if file, err = os.Open(myPath); err != nil {
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
reflectVal = reflect.ValueOf(*f)
|
||||
|
||||
for attrNm, attrVal := range AttrNameValueMap {
|
||||
fieldVal = reflectVal.FieldByName(attrNm)
|
||||
if fieldVal.Bool() {
|
||||
if err = chattr.SetAttr(file, attrVal); err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if err = chattr.UnsetAttr(file, attrVal); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
71
fsutils/funcs_test.go
Normal file
71
fsutils/funcs_test.go
Normal file
@ -0,0 +1,71 @@
|
||||
package fsutils
|
||||
|
||||
import (
|
||||
`errors`
|
||||
`fmt`
|
||||
`os`
|
||||
`os/user`
|
||||
`testing`
|
||||
|
||||
`r00t2.io/sysutils/paths`
|
||||
)
|
||||
|
||||
var (
|
||||
testFilename string = "testfile"
|
||||
testErrBadUser error = errors.New("test must be run as root, on Linux")
|
||||
)
|
||||
|
||||
func testChkUser() (err error) {
|
||||
var u *user.User
|
||||
|
||||
if u, err = user.Current(); err != nil {
|
||||
return
|
||||
}
|
||||
if u.Uid != "0" {
|
||||
err = testErrBadUser
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func TestSetAttrs(t *testing.T) {
|
||||
|
||||
var err error
|
||||
var attrs *FsAttrs
|
||||
|
||||
if attrs, err = GetAttrs(testFilename); err != nil {
|
||||
t.Fatalf("Failed to get attrs for %v: %v", testFilename, err)
|
||||
}
|
||||
t.Logf("Attrs for %v:\n%#v", testFilename, attrs)
|
||||
attrs.CompressFile = true
|
||||
if err = attrs.Apply(testFilename); err != nil {
|
||||
t.Fatalf("Failed to apply attrs to %v: %v", testFilename, err)
|
||||
}
|
||||
t.Logf("Applied new attrs to %v:\n%#v", testFilename, attrs)
|
||||
}
|
||||
|
||||
func TestMain(t *testing.M) {
|
||||
|
||||
var err error
|
||||
var rslt int
|
||||
|
||||
if err = testChkUser(); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if err = paths.RealPath(&testFilename); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(2)
|
||||
}
|
||||
if err = os.WriteFile(testFilename, []byte("This is a test file."), 0o0644); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(3)
|
||||
}
|
||||
|
||||
rslt = t.Run()
|
||||
|
||||
if err = os.Remove(testFilename); err != nil {
|
||||
fmt.Printf("Failed to remove test file %v: %v", testFilename, err)
|
||||
}
|
||||
os.Exit(rslt)
|
||||
}
|
32
fsutils/types.go
Normal file
32
fsutils/types.go
Normal file
@ -0,0 +1,32 @@
|
||||
package fsutils
|
||||
|
||||
// FsAttrs is a convenience struct around github.com/g0rbe/go-chattr.
|
||||
type FsAttrs struct {
|
||||
SecureDelete bool
|
||||
UnDelete bool
|
||||
CompressFile bool
|
||||
SyncUpdatechattr bool
|
||||
Immutable bool
|
||||
AppendOnly bool
|
||||
NoDumpFile bool
|
||||
NoUpdateAtime bool
|
||||
IsDirty bool
|
||||
CompressedClusters bool
|
||||
NoCompress bool
|
||||
EncFile bool
|
||||
BtreeFmt bool
|
||||
HashIdxDir bool
|
||||
AfsDir bool
|
||||
ReservedExt3 bool
|
||||
NoMergeTail bool
|
||||
DirSync bool
|
||||
DirTop bool
|
||||
ReservedExt4a bool
|
||||
Extents bool
|
||||
LargeEaInode bool
|
||||
ReservedExt4b bool
|
||||
NoCOWFile bool
|
||||
ReservedExt4c bool
|
||||
UseParentProjId bool
|
||||
ReservedExt2 bool
|
||||
}
|
7
go.mod
7
go.mod
@ -1,3 +1,8 @@
|
||||
module r00t2.io/sysutils
|
||||
|
||||
go 1.16
|
||||
go 1.21
|
||||
|
||||
require github.com/g0rbe/go-chattr v1.0.1
|
||||
|
||||
// Pending https://github.com/g0rbe/go-chattr/pull/3
|
||||
replace github.com/g0rbe/go-chattr => github.com/johnnybubonic/go-chattr v0.0.0-20240126141003-459f46177b13
|
||||
|
Loading…
Reference in New Issue
Block a user