Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added parsing for fsnotify mask and object type #408

Merged
merged 1 commit into from
Mar 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
144 changes: 144 additions & 0 deletions helpers/argumentParsers.go
Original file line number Diff line number Diff line change
Expand Up @@ -3443,3 +3443,147 @@ func ParseVmFlags(rawValue uint64) VmFlag {

return VmFlag{stringValue: strings.Join(f, "|"), rawValue: rawValue}
}

// =====================================================

// FsNotifyMask represents the event mask used by the dnotify, inotify and fanotify APIs
type FsNotifyMask struct {
rawValue uint64
stringValue string
}

const FsNotifyMaskShiftMax = 30

// revive:disable

// These values are copied from include/linux/fsnotify_backend.h
var (
FS_ACCESS = FsNotifyMask{rawValue: 0x00000001, stringValue: "FS_ACCESS"}
FS_MODIFY = FsNotifyMask{rawValue: 0x00000002, stringValue: "FS_MODIFY"}
FS_ATTRIB = FsNotifyMask{rawValue: 0x00000004, stringValue: "FS_ATTRIB"}
FS_CLOSE_WRITE = FsNotifyMask{rawValue: 0x00000008, stringValue: "FS_CLOSE_WRITE"}
FS_CLOSE_NOWRITE = FsNotifyMask{rawValue: 0x00000010, stringValue: "FS_CLOSE_NOWRITE"}
FS_OPEN = FsNotifyMask{rawValue: 0x00000020, stringValue: "FS_CLOSE_OPEN"}
FS_MOVED_FROM = FsNotifyMask{rawValue: 0x00000040, stringValue: "FS_MOVED_FROM"}
FS_MOVED_TO = FsNotifyMask{rawValue: 0x00000080, stringValue: "FS_MOVED_TO"}
FS_CREATE = FsNotifyMask{rawValue: 0x00000100, stringValue: "FS_CREATE"}
FS_DELETE = FsNotifyMask{rawValue: 0x00000200, stringValue: "FS_DELETE"}
FS_DELETE_SELF = FsNotifyMask{rawValue: 0x00000400, stringValue: "FS_DELETE_SELF"}
FS_MOVE_SELF = FsNotifyMask{rawValue: 0x00000800, stringValue: "FS_MOVE_SELF"}
FS_OPEN_EXEC = FsNotifyMask{rawValue: 0x00001000, stringValue: "FS_OPEN_EXEC"}
FS_UNMOUNT = FsNotifyMask{rawValue: 0x00002000, stringValue: "FS_UNMOUNT"}
FS_Q_OVERFLOW = FsNotifyMask{rawValue: 0x00004000, stringValue: "FS_Q_OVERFLOW"}
FS_ERROR = FsNotifyMask{rawValue: 0x00008000, stringValue: "FS_ERROR"}
FS_OPEN_PERM = FsNotifyMask{rawValue: 0x00010000, stringValue: "FS_OPEN_PERM"}
FS_ACCESS_PERM = FsNotifyMask{rawValue: 0x00020000, stringValue: "FS_ACCESS_PERM"}
FS_OPEN_EXEC_PERM = FsNotifyMask{rawValue: 0x00040000, stringValue: "FS_OPEN_EXEC_PERM"}
FS_EVENT_ON_CHILD = FsNotifyMask{rawValue: 0x08000000, stringValue: "FS_EVENT_ON_CHILD"}
FS_RENAME = FsNotifyMask{rawValue: 0x10000000, stringValue: "FS_RENAME"}
FS_DN_MULTISHOT = FsNotifyMask{rawValue: 0x20000000, stringValue: "FS_DN_MULTISHOT"}
FS_ISDIR = FsNotifyMask{rawValue: 0x40000000, stringValue: "FS_ISDIR"}
)

// revive:enable

var FsNotifyMaskMap = map[uint64]FsNotifyMask{
FS_ACCESS.Value(): FS_ACCESS,
FS_MODIFY.Value(): FS_MODIFY,
FS_ATTRIB.Value(): FS_ATTRIB,
FS_CLOSE_WRITE.Value(): FS_CLOSE_WRITE,
FS_CLOSE_NOWRITE.Value(): FS_CLOSE_NOWRITE,
FS_OPEN.Value(): FS_OPEN,
FS_MOVED_FROM.Value(): FS_MOVED_FROM,
FS_MOVED_TO.Value(): FS_MOVED_TO,
FS_CREATE.Value(): FS_CREATE,
FS_DELETE.Value(): FS_DELETE,
FS_DELETE_SELF.Value(): FS_DELETE_SELF,
FS_MOVE_SELF.Value(): FS_MOVE_SELF,
FS_OPEN_EXEC.Value(): FS_OPEN_EXEC,
FS_UNMOUNT.Value(): FS_UNMOUNT,
FS_Q_OVERFLOW.Value(): FS_Q_OVERFLOW,
FS_ERROR.Value(): FS_ERROR,
FS_OPEN_PERM.Value(): FS_OPEN_PERM,
FS_ACCESS_PERM.Value(): FS_ACCESS_PERM,
FS_OPEN_EXEC_PERM.Value(): FS_OPEN_EXEC_PERM,
FS_EVENT_ON_CHILD.Value(): FS_EVENT_ON_CHILD,
FS_RENAME.Value(): FS_RENAME,
FS_DN_MULTISHOT.Value(): FS_DN_MULTISHOT,
FS_ISDIR.Value(): FS_ISDIR,
}

func (mask FsNotifyMask) Value() uint64 {
return mask.rawValue
}

func (mask FsNotifyMask) String() string {
return mask.stringValue
}

// ParseFsNotifyMask parses the event mask used by the dnotify, inotify and fanotify APIs
func ParseFsNotifyMask(rawValue uint64) FsNotifyMask {
var f []string
for i := 0; i <= FsNotifyMaskShiftMax; i++ {
var mask uint64 = 1 << i

if (rawValue & mask) != 0 {
flag, ok := FsNotifyMaskMap[mask]
if ok {
f = append(f, flag.String())
} else {
f = append(
f,
fmt.Sprintf(
"UNKNOWN_FLAG_0X%s",
strings.ToUpper(strconv.FormatUint(mask, 16)),
),
)
}
}
}

return FsNotifyMask{stringValue: strings.Join(f, "|"), rawValue: rawValue}
}

// =====================================================

// FsNotifyObjType represents the type of filesystem object being watched
type FsNotifyObjType struct {
rawValue uint32
stringValue string
}

// revive:disable

// These values are copied from include/linux/fsnotify_backend.h
var (
FSNOTIFY_OBJ_TYPE_INODE = FsNotifyObjType{rawValue: 0, stringValue: "FSNOTIFY_OBJ_TYPE_INODE"}
FSNOTIFY_OBJ_TYPE_VFSMOUNT = FsNotifyObjType{rawValue: 1, stringValue: "FSNOTIFY_OBJ_TYPE_VFSMOUNT"}
FSNOTIFY_OBJ_TYPE_SB = FsNotifyObjType{rawValue: 2, stringValue: "FSNOTIFY_OBJ_TYPE_SB"}
FSNOTIFY_OBJ_TYPE_DETACHED = FsNotifyObjType{rawValue: 3, stringValue: "FSNOTIFY_OBJ_TYPE_DETACHED"}
)

// revive:enable

var fsNotifyObjTypeMap = map[uint64]FsNotifyObjType{
FSNOTIFY_OBJ_TYPE_INODE.Value(): FSNOTIFY_OBJ_TYPE_INODE,
FSNOTIFY_OBJ_TYPE_VFSMOUNT.Value(): FSNOTIFY_OBJ_TYPE_VFSMOUNT,
FSNOTIFY_OBJ_TYPE_SB.Value(): FSNOTIFY_OBJ_TYPE_SB,
FSNOTIFY_OBJ_TYPE_DETACHED.Value(): FSNOTIFY_OBJ_TYPE_DETACHED,
}

func (objType FsNotifyObjType) Value() uint64 {
return uint64(objType.rawValue)
}

func (objType FsNotifyObjType) String() string {
return objType.stringValue
}

// ParseFsNotifyObjType parses the filesystem object type of an fsnotify watch
func ParseFsNotifyObjType(rawValue uint64) (FsNotifyObjType, error) {
v, ok := fsNotifyObjTypeMap[rawValue]
if !ok {
return FsNotifyObjType{}, fmt.Errorf("not a valid argument: %d", rawValue)
}
return v, nil
}
60 changes: 60 additions & 0 deletions helpers/argumentParsers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -299,3 +299,63 @@ func TestParseVmFlags(t *testing.T) {
})
}
}

func TestParseFsNotifyMask(t *testing.T) {
testCases := []struct {
name string
parseValue uint64
expectedSting string
}{
{
name: "Single value",
parseValue: FS_CREATE.Value(),
expectedSting: "FS_CREATE",
},
{
name: "Multiple values",
parseValue: FS_OPEN_PERM.Value() | FS_ACCESS_PERM.Value() | FS_OPEN_EXEC_PERM.Value(),
expectedSting: "FS_OPEN_PERM|FS_ACCESS_PERM|FS_OPEN_EXEC_PERM",
},
}

for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
flags := ParseFsNotifyMask(testCase.parseValue)
assert.Equal(t, testCase.expectedSting, flags.String())
})
}
}

func TestParseFsNotifyObjType(t *testing.T) {
testCases := []struct {
name string
parseValue uint64
expectedSting string
expectedError bool
}{
{
name: "Type vfsmount",
parseValue: FSNOTIFY_OBJ_TYPE_VFSMOUNT.Value(),
expectedSting: "FSNOTIFY_OBJ_TYPE_VFSMOUNT",
expectedError: false,
},
{
name: "Non existing type",
parseValue: 10000000,
expectedSting: "",
expectedError: true,
},
}

for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
opt, err := ParseFsNotifyObjType(testCase.parseValue)
if testCase.expectedError {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
assert.Equal(t, testCase.expectedSting, opt.String())
})
}
}