v1.9.0
IMPROVED: * Removed *BROKEN* dep. lrn2fixurshitk
This commit is contained in:
parent
9dbc3a00fe
commit
70a88ca8b4
3
.gitignore
vendored
3
.gitignore
vendored
@ -29,6 +29,9 @@
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
# Test file
|
||||
fsutils/testfile
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
|
1
auger/TODO
Normal file
1
auger/TODO
Normal file
@ -0,0 +1 @@
|
||||
This module is still under work.
|
@ -7,3 +7,35 @@ const (
|
||||
augInclTfm string = "incl" // The transformer keyword for Augeas includes.
|
||||
augAppendSuffix string = "[last()+1]"
|
||||
)
|
||||
|
||||
var (
|
||||
dstPtrTrue bool = true
|
||||
dstPtrFalse bool = false
|
||||
)
|
||||
|
||||
var (
|
||||
// PtrTrue and PtrFalse are convenience references for constructing an AugFlags if needed. It is recommended you do not change these values if you do not like being confused.
|
||||
PtrTrue *bool = &dstPtrTrue
|
||||
PtrFalse *bool = &dstPtrFalse
|
||||
)
|
||||
|
||||
/*
|
||||
IncludeOptNone is the default include recursion option for Aug.RecursiveInclude.
|
||||
* No special behavior is defined
|
||||
* All include directives are assumed to refer:
|
||||
* Explicitly/exclusively to file paths
|
||||
* That must exist
|
||||
*/
|
||||
const IncludeOptNone includeOpt = 0
|
||||
const (
|
||||
// IncludeOptNoExist specifies that inclusions are allowed to not exist, otherwise an error will be raised while attempting to parse them.
|
||||
IncludeOptNoExist includeOpt = 1 << iota
|
||||
// IncludeOptGlobbing indicates that the inclusion system supports globbing (as supported by (github.com/gobwas/glob).Match).
|
||||
IncludeOptGlobbing
|
||||
// IncludeOptRegex indicates that the inclusion system supports matching by regex (as supported by regexp).
|
||||
IncludeOptRegex
|
||||
// IncludeOptDirs indicates that the inclusion system supports matching by directory.
|
||||
IncludeOptDirs
|
||||
// IncludeOptDirsRecursive indicates that the inclusion system also recurses into subdirectories of matched directories. Only used if IncludeOptDirs is also set.
|
||||
IncludeOptDirsRecursive
|
||||
)
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
`strings`
|
||||
|
||||
`honnef.co/go/augeas`
|
||||
`r00t2.io/goutils/bitmask`
|
||||
)
|
||||
|
||||
/*
|
||||
@ -41,12 +42,17 @@ func NewAugerFromAugeas(orig augeas.Augeas) (aug *Aug) {
|
||||
}
|
||||
|
||||
/*
|
||||
AugpathToFspath returns the filesystem path from an Augeas path.
|
||||
AugpathToFspath returns the filesystem path (i.e. an existing file) from an Augeas path.
|
||||
|
||||
It is *required* and expected that the Augeas standard /files prefix be removed first;
|
||||
if not, it is assumed to be part of the filesystem path.
|
||||
|
||||
If a valid path cannot be determined, fsPath will be empty.
|
||||
|
||||
To be clear, a file must exist for fsPath to not be empty;
|
||||
the way AugpathToFsPath works is it recurses bottom-up a
|
||||
given path and checks for the existence of a file,
|
||||
continuing upwards if not found.
|
||||
*/
|
||||
func AugpathToFspath(augPath string) (fsPath string, err error) {
|
||||
|
||||
@ -95,3 +101,11 @@ func dedupePaths(new, existing []string) (missing []string) {
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// getInclPaths applies path options to inclusions.
|
||||
func getInclPaths(pathSpec string, inclFlags *bitmask.MaskBit) (fpaths []string, err error) {
|
||||
|
||||
// TODO
|
||||
|
||||
return
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ import (
|
||||
`github.com/davecgh/go-spew/spew`
|
||||
`github.com/google/shlex`
|
||||
`honnef.co/go/augeas`
|
||||
`r00t2.io/goutils/bitmask`
|
||||
`r00t2.io/sysutils/paths`
|
||||
)
|
||||
|
||||
@ -146,10 +147,21 @@ breakCmd:
|
||||
An error will be returned if augLens is a nonexistent or not-loaded Augeas lens module.
|
||||
|
||||
Depending on how many files there are and whether globs vs. explicit filepaths are included, this may take a while.
|
||||
*/
|
||||
func (a *Aug) RecursiveInclude(augLens, includeDirective, fsRoot string) (err error) {
|
||||
|
||||
if err = a.addIncl(includeDirective, augLens, fsRoot, nil); err != nil {
|
||||
optFlags may be nil, multiple includeOpt (see the IncludeOpt* constants) as variadic parameters/expanded slice,
|
||||
bitwise-OR'd together, or multiple non-OR'd and OR'd together (all will be combined to a single value).
|
||||
*/
|
||||
func (a *Aug) RecursiveInclude(augLens, includeDirective, fsRoot string, optFlags ...includeOpt) (err error) {
|
||||
|
||||
var flags *bitmask.MaskBit = bitmask.NewMaskBit()
|
||||
|
||||
if optFlags != nil && len(optFlags) > 0 {
|
||||
for _, f := range optFlags {
|
||||
flags.AddFlag(f.toMb())
|
||||
}
|
||||
}
|
||||
|
||||
if err = a.addIncl(includeDirective, augLens, fsRoot, nil, flags); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@ -164,14 +176,16 @@ func (a *Aug) RecursiveInclude(augLens, includeDirective, fsRoot string) (err er
|
||||
newInclPaths are new filesystem paths/Augeas-compatible glob patterns to load into the filetree and recurse into.
|
||||
They may be nil, especially if the first run.
|
||||
*/
|
||||
func (a *Aug) addIncl(includeDirective, augLens string, fsRoot string, newInclPaths []string) (err error) {
|
||||
func (a *Aug) addIncl(includeDirective, augLens string, fsRoot string, newInclPaths []string, inclFlags *bitmask.MaskBit) (err error) {
|
||||
|
||||
var matches []string // Passed around set of Augeas matches.
|
||||
var exists bool // Used to indicate if the include path exists.
|
||||
var includes []string // Filepath(s)/glob(s) from fetching includeDirective in lensInclPath. These are internal to the application but are recursed.
|
||||
var lensInclPath string // The path of the included paths in the tree. These are internal to Augeas, not the application.
|
||||
var appendPath string // The path for new Augeas includes.
|
||||
var match []string // A placeholder for iterating when populating includes.
|
||||
var fpath string // A placeholder for finding the path of a conf file that contains an includeDirective.
|
||||
var normalizedIncludes []string // A temporary slice to hold normalization operations and other dynamic building.
|
||||
var lensPath string = fmt.Sprintf(augLensTpl, augLens) // The path of the lens (augLens) itself.
|
||||
var augErr *augeas.Error = new(augeas.Error) // We use this to skip "nonexistent" lens.
|
||||
|
||||
@ -193,7 +207,7 @@ func (a *Aug) addIncl(includeDirective, augLens string, fsRoot string, newInclPa
|
||||
|
||||
// First canonize paths.
|
||||
if newInclPaths != nil && len(newInclPaths) > 0 {
|
||||
// Existing includes. We don't return on an empty lensInclPath because
|
||||
// Existing includes. We don't return on an empty lensInclPath.
|
||||
if matches, err = a.aug.Match(lensInclPath); err != nil {
|
||||
if errors.As(err, augErr) && augErr.Code == augeas.NoMatch {
|
||||
err = nil
|
||||
@ -221,6 +235,17 @@ func (a *Aug) addIncl(includeDirective, augLens string, fsRoot string, newInclPa
|
||||
// We don't want to bother adding multiple incl's for the same path(s); it can negatively affect Augeas loads.
|
||||
newInclPaths = dedupePaths(newInclPaths, matches)
|
||||
|
||||
// And then apply things like recursion, globbing, etc.
|
||||
normalizedIncludes = make([]string, 0, len(newInclPaths))
|
||||
if inclFlags.HasFlag(IncludeOptGlobbing.toMb()) {
|
||||
// TODO
|
||||
/*
|
||||
if strings.Contains(newInclPaths[idx], "*") {
|
||||
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
// Add the new path(s) as Augeas include entries.
|
||||
if newInclPaths != nil {
|
||||
for _, fsPath := range newInclPaths {
|
||||
@ -285,10 +310,13 @@ func (a *Aug) addIncl(includeDirective, augLens string, fsRoot string, newInclPa
|
||||
}
|
||||
|
||||
if matches != nil && len(matches) != 0 {
|
||||
if err = a.addIncl(includeDirective, augLens, fsRoot, matches); err != nil {
|
||||
if err = a.addIncl(includeDirective, augLens, fsRoot, matches, inclFlags); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// TODO
|
||||
_, _ = exists, normalizedIncludes
|
||||
|
||||
return
|
||||
}
|
||||
|
13
auger/funcs_includeopt.go
Normal file
13
auger/funcs_includeopt.go
Normal file
@ -0,0 +1,13 @@
|
||||
package auger
|
||||
|
||||
import (
|
||||
`r00t2.io/goutils/bitmask`
|
||||
)
|
||||
|
||||
// toMb returns a bitmask.MaskBit of this includeOpt.
|
||||
func (i includeOpt) toMb() (mb bitmask.MaskBit) {
|
||||
|
||||
mb = bitmask.MaskBit(i)
|
||||
|
||||
return
|
||||
}
|
@ -22,3 +22,18 @@ func TestNewAuger(t *testing.T) {
|
||||
|
||||
_ = aug
|
||||
}
|
||||
|
||||
func TestRecursiveInclude(t *testing.T) {
|
||||
|
||||
var aug *Aug
|
||||
var err error
|
||||
|
||||
if aug, err = NewAuger("/", "", &AugFlags{DryRun: PtrTrue}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// This requires Nginx to be installed and with a particularly complex nested include system.
|
||||
if err = aug.RecursiveInclude("Nginx", "include", "/etc/nginx"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
@ -2,8 +2,11 @@ package auger
|
||||
|
||||
import (
|
||||
`honnef.co/go/augeas`
|
||||
`r00t2.io/goutils/bitmask`
|
||||
)
|
||||
|
||||
type includeOpt bitmask.MaskBit
|
||||
|
||||
// Aug is a wrapper around (honnef.co/go/)augeas.Augeas. Remember to call Aug.Close().
|
||||
type Aug struct {
|
||||
aug augeas.Augeas
|
||||
|
@ -3,3 +3,4 @@
|
||||
|
||||
It is now its own module: r00t2.io/cryptparse
|
||||
*/
|
||||
package cryptparse
|
||||
|
@ -159,7 +159,7 @@ func GetPidEnvMap(pid uint32) (envMap map[string]string, err error) {
|
||||
var procPath string
|
||||
var exists bool
|
||||
|
||||
envMap = make(map[string]string, 0)
|
||||
envMap = make(map[string]string)
|
||||
|
||||
procPath = fmt.Sprintf("/proc/%v/environ", pid)
|
||||
|
||||
|
@ -13,7 +13,7 @@ func envListToMap(envs []string) (envMap map[string]string) {
|
||||
var kv []string
|
||||
var k, v string
|
||||
|
||||
envMap = make(map[string]string, 0)
|
||||
envMap = make(map[string]string)
|
||||
|
||||
for _, ev := range envs {
|
||||
kv = strings.SplitN(ev, "=", 2)
|
||||
|
@ -1,101 +0,0 @@
|
||||
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",
|
||||
}
|
||||
)
|
126
fsutils/consts_linux.go
Normal file
126
fsutils/consts_linux.go
Normal file
@ -0,0 +1,126 @@
|
||||
//go:build linux
|
||||
|
||||
package fsutils
|
||||
|
||||
// https://github.com/torvalds/linux/blob/master/include/uapi/linux/fs.h "Inode flags (FS_IOC_GETFLAGS / FS_IOC_SETFLAGS)"
|
||||
const (
|
||||
SecureDelete fsAttr = 1 << iota // Secure deletion
|
||||
UnDelete // Undelete
|
||||
CompressFile // Compress file
|
||||
SyncUpdate // Synchronous updates
|
||||
Immutable // Immutable file
|
||||
AppendOnly // Writes to file may only append
|
||||
NoDumpFile // Do not dump file
|
||||
NoUpdateAtime // Do not update atime
|
||||
IsDirty // Nobody knows what this does, lol.
|
||||
CompressedClusters // One or more compressed clusters
|
||||
NoCompress // Don't compress
|
||||
EncFile // Encrypted file
|
||||
BtreeFmt // Btree format dir
|
||||
AfsDir // AFS directory
|
||||
ReservedExt3 // Reserved for ext3
|
||||
NoMergeTail // File tail should not be merged
|
||||
DirSync // dirsync behaviour (directories only)
|
||||
DirTop // Top of directory hierarchies
|
||||
ReservedExt4a // Reserved for ext4
|
||||
Extents // Extents
|
||||
VerityProtected // Verity-protected inode
|
||||
LargeEaInode // Inode used for large EA
|
||||
ReservedExt4b // Reserved for ext4
|
||||
NoCOWFile // Do not cow file
|
||||
_ // (Unused)
|
||||
DAX // Inode is DAX
|
||||
_ // (Unused)
|
||||
_ // (Unused)
|
||||
ReservedExt4c // Reserved for ext4
|
||||
UseParentProjId // Create with parents projid
|
||||
CaseInsensitive // Folder is case-insensitive
|
||||
ReservedExt2 // Reserved for ext2 lib
|
||||
)
|
||||
|
||||
// These are the same value. For some reason.
|
||||
const (
|
||||
HashIdxDir fsAttr = BtreeFmt // Hash-indexed directory
|
||||
)
|
||||
|
||||
var (
|
||||
// AttrNameValueMap contains a mapping of attribute names (as designated by this package) to their flag values.
|
||||
AttrNameValueMap map[string]fsAttr = map[string]fsAttr{
|
||||
"SecureDelete": SecureDelete,
|
||||
"UnDelete": UnDelete,
|
||||
"CompressFile": CompressFile,
|
||||
"SyncUpdate": SyncUpdate,
|
||||
"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,
|
||||
"VerityProtected": VerityProtected,
|
||||
"LargeEaInode": LargeEaInode,
|
||||
"ReservedExt4b": ReservedExt4b,
|
||||
"NoCOWFile": NoCOWFile,
|
||||
"DAX": DAX,
|
||||
"ReservedExt4c": ReservedExt4c,
|
||||
"UseParentProjId": UseParentProjId,
|
||||
"CaseInsensitive": CaseInsensitive,
|
||||
"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 their string value is unpredictable.
|
||||
*/
|
||||
AttrValueNameMap map[fsAttr]string = invertMap(AttrNameValueMap)
|
||||
|
||||
// KernelNameValueMap allows lookups using the symbol name as used in the Linux kernel source.
|
||||
KernelNameValueMap map[string]fsAttr = map[string]fsAttr{
|
||||
"FS_SECRM_FL": SecureDelete,
|
||||
"FS_UNRM_FL": UnDelete,
|
||||
"FS_COMPR_FL": CompressFile,
|
||||
"FS_SYNC_FL": SyncUpdate,
|
||||
"FS_IMMUTABLE_FL": Immutable,
|
||||
"FS_APPEND_FL": AppendOnly,
|
||||
"FS_NODUMP_FL": NoDumpFile,
|
||||
"FS_NOATIME_FL": NoUpdateAtime,
|
||||
"FS_DIRTY_FL": IsDirty,
|
||||
"FS_COMPRBLK_FL": CompressedClusters,
|
||||
"FS_NOCOMP_FL": NoCompress,
|
||||
"FS_ENCRYPT_FL": EncFile,
|
||||
"FS_BTREE_FL": BtreeFmt,
|
||||
"FS_INDEX_FL": HashIdxDir,
|
||||
"FS_IMAGIC_FL": AfsDir,
|
||||
"FS_JOURNAL_DATA_FL": ReservedExt3,
|
||||
"FS_NOTAIL_FL": NoMergeTail,
|
||||
"FS_DIRSYNC_FL": DirSync,
|
||||
"FS_TOPDIR_FL": DirTop,
|
||||
"FS_HUGE_FILE_FL": ReservedExt4a,
|
||||
"FS_EXTENT_FL": Extents,
|
||||
"FS_VERITY_FL": VerityProtected,
|
||||
"FS_EA_INODE_FL": LargeEaInode,
|
||||
"FS_EOFBLOCKS_FL": ReservedExt4b,
|
||||
"FS_NOCOW_FL": NoCOWFile,
|
||||
"FS_DAX_FL": DAX,
|
||||
"FS_INLINE_DATA_FL": ReservedExt4c,
|
||||
"FS_PROJINHERIT_FL": UseParentProjId,
|
||||
"FS_CASEFOLD_FL": CaseInsensitive,
|
||||
"FS_RESERVED_FL": ReservedExt2,
|
||||
}
|
||||
|
||||
/*
|
||||
KernelValueNameMap contains a mapping of attribute flags to their kernel source symbol name.
|
||||
Note the oddball here, BtreeFmt and HashIdxDir are actually the same value, so their string value is unpredictable.
|
||||
*/
|
||||
KernelValueNameMap map[fsAttr]string = invertMap(KernelNameValueMap)
|
||||
)
|
7
fsutils/doc.go
Normal file
7
fsutils/doc.go
Normal file
@ -0,0 +1,7 @@
|
||||
/*
|
||||
fsutils is a collection of filesystem-related functions, types, etc.
|
||||
|
||||
Currently it's only a (fixed/actually working) reimplementation of github.com/g0rbe/go-chattr.
|
||||
(Note to library maintainers, if someone reports an integer overflow and even tells you how to fix it, you should probably fix it.)
|
||||
*/
|
||||
package fsutils
|
@ -1,44 +0,0 @@
|
||||
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
|
||||
}
|
@ -1,10 +1,11 @@
|
||||
//go:build linux
|
||||
|
||||
package fsutils
|
||||
|
||||
import (
|
||||
`os`
|
||||
`reflect`
|
||||
|
||||
`github.com/g0rbe/go-chattr`
|
||||
`r00t2.io/sysutils/paths`
|
||||
)
|
||||
|
||||
@ -29,11 +30,11 @@ func (f *FsAttrs) Apply(path string) (err error) {
|
||||
for attrNm, attrVal := range AttrNameValueMap {
|
||||
fieldVal = reflectVal.FieldByName(attrNm)
|
||||
if fieldVal.Bool() {
|
||||
if err = chattr.SetAttr(file, attrVal); err != nil {
|
||||
if err = setAttrs(file, attrVal); err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if err = chattr.UnsetAttr(file, attrVal); err != nil {
|
||||
if err = unsetAttrs(file, attrVal); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
149
fsutils/funcs_linux.go
Normal file
149
fsutils/funcs_linux.go
Normal file
@ -0,0 +1,149 @@
|
||||
//go:build linux
|
||||
|
||||
package fsutils
|
||||
|
||||
import (
|
||||
`os`
|
||||
`reflect`
|
||||
`unsafe`
|
||||
|
||||
`golang.org/x/sys/unix`
|
||||
`r00t2.io/goutils/bitmask`
|
||||
`r00t2.io/sysutils/paths`
|
||||
)
|
||||
|
||||
func GetAttrs(path string) (attrs *FsAttrs, err error) {
|
||||
|
||||
var f *os.File
|
||||
var evalAttrs FsAttrs
|
||||
var attrVal fsAttr
|
||||
var attrValBit bitmask.MaskBit
|
||||
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 = getAttrs(f); err != nil {
|
||||
return
|
||||
}
|
||||
attrValBit = bitmask.MaskBit(attrVal)
|
||||
|
||||
for attrNm, attrInt := range AttrNameValueMap {
|
||||
field = reflectVal.FieldByName(attrNm)
|
||||
field.SetBool(attrValBit.HasFlag(bitmask.MaskBit(attrInt)))
|
||||
}
|
||||
|
||||
attrs = new(FsAttrs)
|
||||
*attrs = evalAttrs
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// getAttrs is the unexported low-level syscall to get attributes.
|
||||
func getAttrs(f *os.File) (attrVal fsAttr, err error) {
|
||||
|
||||
var u uint
|
||||
var curFlags int
|
||||
// var errNo syscall.Errno
|
||||
|
||||
/*
|
||||
if _, _, errNo = unix.Syscall(unix.SYS_IOCTL, f.Fd(), unix.FS_IOC_GETFLAGS, uintptr(unsafe.Pointer(&curFlags))); errNo != 0 {
|
||||
err = os.NewSyscallError("ioctl: FS_IOC_GETFLAGS", errNo)
|
||||
return
|
||||
}
|
||||
*/
|
||||
if curFlags, err = unix.IoctlGetInt(int(f.Fd()), unix.FS_IOC_GETFLAGS); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
u = uint(curFlags)
|
||||
|
||||
attrVal = fsAttr(u)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// invertMap returns some handy consts remapping for easier lookups.
|
||||
func invertMap(origMap map[string]fsAttr) (newMap map[fsAttr]string) {
|
||||
|
||||
if origMap == nil {
|
||||
return
|
||||
}
|
||||
newMap = make(map[fsAttr]string)
|
||||
|
||||
for k, v := range origMap {
|
||||
newMap[v] = k
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// setAttrs is the unexported low-level syscall to set attributes. attrs may be OR'd.
|
||||
func setAttrs(f *os.File, attrs fsAttr) (err error) {
|
||||
|
||||
var curAttrs fsAttr
|
||||
var ab bitmask.MaskBit
|
||||
var errNo unix.Errno
|
||||
var val uint
|
||||
|
||||
if curAttrs, err = getAttrs(f); err != nil {
|
||||
return
|
||||
}
|
||||
ab = bitmask.MaskBit(curAttrs)
|
||||
|
||||
if ab.HasFlag(bitmask.MaskBit(attrs)) {
|
||||
return
|
||||
}
|
||||
|
||||
ab.AddFlag(bitmask.MaskBit(attrs))
|
||||
|
||||
val = ab.Value()
|
||||
|
||||
/*
|
||||
if err = unix.IoctlSetInt(int(f.Fd()), unix.FS_IOC_SETFLAGS, int(ab.Value())); err != nil {
|
||||
return
|
||||
}
|
||||
*/
|
||||
if _, _, errNo = unix.Syscall(unix.SYS_IOCTL, f.Fd(), unix.FS_IOC_SETFLAGS, uintptr(unsafe.Pointer(&val))); errNo != 0 {
|
||||
err = os.NewSyscallError("ioctl: SYS_IOCTL", errNo)
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// unsetAttrs is the unexported low-level syscall to remove attributes. attrs may be OR'd.
|
||||
func unsetAttrs(f *os.File, attrs fsAttr) (err error) {
|
||||
|
||||
var curAttrs fsAttr
|
||||
var ab bitmask.MaskBit
|
||||
|
||||
if curAttrs, err = getAttrs(f); err != nil {
|
||||
return
|
||||
}
|
||||
ab = bitmask.MaskBit(curAttrs)
|
||||
|
||||
if !ab.HasFlag(bitmask.MaskBit(attrs)) {
|
||||
return
|
||||
}
|
||||
|
||||
ab.ClearFlag(bitmask.MaskBit(attrs))
|
||||
|
||||
/*
|
||||
if err = unix.IoctlSetInt(int(f.Fd()), unix.FS_IOC_SETFLAGS, int(ab.Value())); err != nil {
|
||||
return
|
||||
}
|
||||
*/
|
||||
|
||||
return
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
//go:build linux
|
||||
|
||||
package fsutils
|
||||
|
||||
import (
|
||||
@ -7,12 +9,13 @@ import (
|
||||
`os/user`
|
||||
`testing`
|
||||
|
||||
`github.com/davecgh/go-spew/spew`
|
||||
`r00t2.io/sysutils/paths`
|
||||
)
|
||||
|
||||
var (
|
||||
testFilename string = "testfile"
|
||||
testErrBadUser error = errors.New("test must be run as root, on Linux")
|
||||
testErrBadUser error = errors.New("test must be run as root")
|
||||
)
|
||||
|
||||
func testChkUser() (err error) {
|
||||
@ -36,12 +39,18 @@ func TestSetAttrs(t *testing.T) {
|
||||
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)
|
||||
t.Logf("Attrs for %v (before):\n%s", testFilename, spew.Sdump(attrs))
|
||||
attrs.CompressFile = true
|
||||
attrs.SyncUpdate = true
|
||||
attrs.SecureDelete = 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)
|
||||
if attrs, err = GetAttrs(testFilename); err != nil {
|
||||
t.Fatalf("Failed to get attrs for %v: %v", testFilename, err)
|
||||
}
|
||||
t.Logf("Attrs for %v (after):\n%s", testFilename, spew.Sdump(attrs))
|
||||
}
|
||||
|
||||
func TestMain(t *testing.M) {
|
@ -1,11 +1,17 @@
|
||||
package fsutils
|
||||
|
||||
// FsAttrs is a convenience struct around github.com/g0rbe/go-chattr.
|
||||
import (
|
||||
`r00t2.io/goutils/bitmask`
|
||||
)
|
||||
|
||||
type fsAttr bitmask.MaskBit
|
||||
|
||||
// FsAttrs is a struct representation of filesystem attributes on Linux.
|
||||
type FsAttrs struct {
|
||||
SecureDelete bool
|
||||
UnDelete bool
|
||||
CompressFile bool
|
||||
SyncUpdatechattr bool
|
||||
SyncUpdate bool
|
||||
Immutable bool
|
||||
AppendOnly bool
|
||||
NoDumpFile bool
|
||||
@ -23,10 +29,13 @@ type FsAttrs struct {
|
||||
DirTop bool
|
||||
ReservedExt4a bool
|
||||
Extents bool
|
||||
VerityProtected bool
|
||||
LargeEaInode bool
|
||||
ReservedExt4b bool
|
||||
NoCOWFile bool
|
||||
DAX bool
|
||||
ReservedExt4c bool
|
||||
UseParentProjId bool
|
||||
CaseInsensitive bool
|
||||
ReservedExt2 bool
|
||||
}
|
||||
|
6
go.mod
6
go.mod
@ -4,12 +4,8 @@ go 1.23.2
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
github.com/g0rbe/go-chattr v1.0.1
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
||||
golang.org/x/sys v0.26.0
|
||||
honnef.co/go/augeas v0.0.0-20161110001225-ca62e35ed6b8
|
||||
r00t2.io/goutils v1.7.0
|
||||
r00t2.io/goutils v1.7.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
|
||||
|
2
go.sum
2
go.sum
@ -13,6 +13,8 @@ honnef.co/go/augeas v0.0.0-20161110001225-ca62e35ed6b8 h1:FW42yWB1sGClqswyHIB68w
|
||||
honnef.co/go/augeas v0.0.0-20161110001225-ca62e35ed6b8/go.mod h1:44w9OfBSQ9l3o59rc2w3AnABtE44bmtNnRMNC7z+oKE=
|
||||
r00t2.io/goutils v1.7.0 h1:iQluWlkOyBwOKaK94D5QSnSMYpGKtMb/5WjefmdfHgI=
|
||||
r00t2.io/goutils v1.7.0/go.mod h1:9ObJI9S71wDLTOahwoOPs19DhZVYrOh4LEHmQ8SW4Lk=
|
||||
r00t2.io/goutils v1.7.1 h1:Yzl9rxX1sR9WT0FcjK60qqOgBoFBOGHYKZVtReVLoQc=
|
||||
r00t2.io/goutils v1.7.1/go.mod h1:9ObJI9S71wDLTOahwoOPs19DhZVYrOh4LEHmQ8SW4Lk=
|
||||
r00t2.io/sysutils v1.1.1/go.mod h1:Wlfi1rrJpoKBOjWiYM9rw2FaiZqraD6VpXyiHgoDo/o=
|
||||
r00t2.io/sysutils v1.7.0 h1:zk5IbcbZvq11FoXI/fLvcgyq36lBhPDY6fvC9CunfWE=
|
||||
r00t2.io/sysutils v1.7.0/go.mod h1:Sk/7riJp9fteeW9STkdQ/k22huL1J6r05n6wLh5byHY=
|
||||
|
Loading…
Reference in New Issue
Block a user