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

Do not suppress Chmod on non-existent file #35

Merged
merged 1 commit into from Mar 6, 2024
Merged

Do not suppress Chmod on non-existent file #35

merged 1 commit into from Mar 6, 2024

Conversation

shogo82148
Copy link
Owner

@shogo82148 shogo82148 commented Mar 6, 2024

port of fsnotify/fsnotify#260

Currently fsnotify suppresses a Chmod event if the file does not exist when the event is received.

This makes it impossible to use fsnotify to detect when an opened file is removed. In such case the Linux kernel sends IN_ATTRIB event, as described in inotify(7) man page:

IN_ATTRIB (*)
Metadata changed—for example, permissions (e.g., chmod(2)),
timestamps (e.g., utimensat(2)), extended attributes (setx‐
attr(2)), link count (since Linux 2.6.25; e.g., for the tar‐
get of link(2) and for unlink(2)), and user/group ID (e.g.,
chown(2)).

(to clarify, in this very case it's link count that changes).

To fix:

  • Modify the code to only suppress MODIFY and CREATE events.
  • Add a test case to verify Chmod event is delivered.

While at it, fix the comment in ignoreLinux() to use the up-to-date terminology (event ops).

A test case is added.

Summary by CodeRabbit

  • Bug Fixes
    • Improved file event handling to focus on Create or Write events for better accuracy in monitoring file changes.
    • Added tests to ensure reliable file deletion event detection.

Currently fsnotify suppresses a Chmod event if the file does not exist
when the event is received.

This makes it impossible to use fsnotify to detect when an opened file
is removed. In such case the Linux kernel sends IN_ATTRIB event,
as described in inotify(7) man page:

> IN_ATTRIB (*)
>        Metadata  changed—for example, permissions (e.g., chmod(2)),
>        timestamps (e.g., utimensat(2)), extended attributes  (setx‐
>        attr(2)), link count (since Linux 2.6.25; e.g., for the tar‐
>        get of link(2) and for unlink(2)), and user/group ID  (e.g.,
>        chown(2)).

(to clarify, in this very case it's link count that changes).

To fix:
 * Modify the code to only suppress MODIFY and CREATE events.
 * Add a test case to verify Chmod event is delivered.

While at it, fix the comment in ignoreLinux() to use the up-to-date
terminology (event ops).

A test case is added.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
Copy link

coderabbitai bot commented Mar 6, 2024

Walkthrough

This update refines the event handling in a Go project by shifting the focus of inotify.go from monitoring DELETE or RENAME events to prioritizing Create or Write events. This adjustment aims to enhance the responsiveness and relevance of the file monitoring system. Additionally, a new test case, TestInotifyDeleteOpenedFile, has been introduced in inotify_test.go to ensure the robustness of file deletion event tracking, incorporating various file operations and event checks.

Changes

Files Summary
inotify.go Updated event handling to focus on Create or Write; adjusted comments accordingly.
inotify_test.go Added TestInotifyDeleteOpenedFile to test file deletion events, covering multiple operations.

🐇✨
In the realm of code, where changes take flight,
A rabbit hopped in, with updates to write.
From Create to Write, it shifted its gaze,
Testing deletions, through the digital maze.
With each line of code, so carefully penned,
The rabbit ensured, on it you can depend.
🌟📂

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

Share

Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>.
    • Generate unit-tests for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit tests for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai generate interesting stats about this repository and render them as a table.
    • @coderabbitai show all the console.log statements in this repository.
    • @coderabbitai read src/utils.ts and generate unit tests.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (invoked as PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger a review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai help to get help.

Additionally, you can add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.

CodeRabbit Configration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • The JSON schema for the configuration file is available here.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/coderabbit-overrides.v2.json

CodeRabbit Discord Community

Join our Discord Community to get help, request features, and share feedback.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 2

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between b408db8 and 03d2b5a.
Files selected for processing (2)
  • inotify.go (1 hunks)
  • inotify_test.go (1 hunks)

Comment on lines +320 to +325
// If the event is Create or Write, the file must exist, or the
// event will be suppressed.
// *Note*: this was put in place because it was seen that a Write
// event was sent after the Remove. This ignores the Write and
// assumes a Remove will come or has come if the file doesn't exist.
if e.Op&Create != 0 || e.Op&Write != 0 {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The update to the ignoreLinux function to focus on suppressing Create or Write events for non-existent files aligns well with the PR's objectives. This change ensures that Chmod events, which can indicate a file's removal, are not suppressed, enhancing the library's ability to detect file deletions.

One minor suggestion for improvement is to clarify the comment further by explicitly stating that Chmod events are not suppressed by this logic, making it clear for future maintainers why this specific change was made.

	// If the event is Create or Write, the file must exist, or the
	// event will be suppressed. This logic specifically allows Chmod
	// events, which can indicate a file's removal, to be processed.
	// *Note*: This was put in place because it was observed that a Write
	// event could be sent after the Remove event. This logic ignores the Write
	// and assumes a Remove will come or has come if the file doesn't exist.

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
// If the event is Create or Write, the file must exist, or the
// event will be suppressed.
// *Note*: this was put in place because it was seen that a Write
// event was sent after the Remove. This ignores the Write and
// assumes a Remove will come or has come if the file doesn't exist.
if e.Op&Create != 0 || e.Op&Write != 0 {
// If the event is Create or Write, the file must exist, or the
// event will be suppressed. This logic specifically allows Chmod
// events, which can indicate a file's removal, to be processed.
// *Note*: This was put in place because it was observed that a Write
// event could be sent after the Remove event. This logic ignores the Write
// and assumes a Remove will come or has come if the file doesn't exist.
if e.Op&Create != 0 || e.Op&Write != 0 {

Comment on lines +499 to +547

func TestInotifyDeleteOpenedFile(t *testing.T) {
testDir := tempMkdir(t)
defer os.RemoveAll(testDir)

testFile := filepath.Join(testDir, "testfile")

// create and open a file
fd, err := os.Create(testFile)
if err != nil {
t.Fatalf("Create failed: %v", err)
}
defer fd.Close()

w, err := NewWatcher()
if err != nil {
t.Fatalf("Failed to create watcher: %v", err)
}
defer w.Close()

err = w.Add(testFile)
if err != nil {
t.Fatalf("Failed to add watch for %s: %v", testFile, err)
}

checkEvent := func(exp Op) {
select {
case event := <-w.Events:
t.Logf("Event received: %s", event.Op)
if event.Op != exp {
t.Fatalf("Event expected: %s, got: %s", exp, event.Op)
}
case <-time.After(100 * time.Millisecond):
t.Fatalf("Expected %s event not received", exp)
}
}

// Remove the (opened) file, check Chmod event (notifying
// about file link count change) is received
err = os.Remove(testFile)
if err != nil {
t.Fatalf("Failed to remove file: %s", err)
}
checkEvent(Chmod)

// Close the file, check Remove event is received
fd.Close()
checkEvent(Remove)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The TestInotifyDeleteOpenedFile function is well-implemented and effectively tests the delivery of Chmod and Remove events upon file deletion. This aligns with the PR's objectives and enhances the test coverage for the fsnotify library.

One suggestion for improvement is to ensure that the watcher (w) is closed even if the test fails before reaching the defer w.Close() statement. This can be achieved by moving the watcher creation and defer statement to the beginning of the test function.

+	w, err := NewWatcher()
+	if err != nil {
+		t.Fatalf("Failed to create watcher: %v", err)
+	}
+	defer w.Close()
+
	fd, err := os.Create(testFile)
	if err != nil {
		t.Fatalf("Create failed: %v", err)
	}
	defer fd.Close()
-	w, err := NewWatcher()
-	if err != nil {
-		t.Fatalf("Failed to create watcher: %v", err)
-	}
-	defer w.Close()

This change ensures that the watcher is always properly closed, preventing potential resource leaks during test execution.


Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
func TestInotifyDeleteOpenedFile(t *testing.T) {
testDir := tempMkdir(t)
defer os.RemoveAll(testDir)
testFile := filepath.Join(testDir, "testfile")
// create and open a file
fd, err := os.Create(testFile)
if err != nil {
t.Fatalf("Create failed: %v", err)
}
defer fd.Close()
w, err := NewWatcher()
if err != nil {
t.Fatalf("Failed to create watcher: %v", err)
}
defer w.Close()
err = w.Add(testFile)
if err != nil {
t.Fatalf("Failed to add watch for %s: %v", testFile, err)
}
checkEvent := func(exp Op) {
select {
case event := <-w.Events:
t.Logf("Event received: %s", event.Op)
if event.Op != exp {
t.Fatalf("Event expected: %s, got: %s", exp, event.Op)
}
case <-time.After(100 * time.Millisecond):
t.Fatalf("Expected %s event not received", exp)
}
}
// Remove the (opened) file, check Chmod event (notifying
// about file link count change) is received
err = os.Remove(testFile)
if err != nil {
t.Fatalf("Failed to remove file: %s", err)
}
checkEvent(Chmod)
// Close the file, check Remove event is received
fd.Close()
checkEvent(Remove)
}
func TestInotifyDeleteOpenedFile(t *testing.T) {
testDir := tempMkdir(t)
defer os.RemoveAll(testDir)
testFile := filepath.Join(testDir, "testfile")
w, err := NewWatcher()
if err != nil {
t.Fatalf("Failed to create watcher: %v", err)
}
defer w.Close()
// create and open a file
fd, err := os.Create(testFile)
if err != nil {
t.Fatalf("Create failed: %v", err)
}
defer fd.Close()
err = w.Add(testFile)
if err != nil {
t.Fatalf("Failed to add watch for %s: %v", testFile, err)
}
checkEvent := func(exp Op) {
select {
case event := <-w.Events:
t.Logf("Event received: %s", event.Op)
if event.Op != exp {
t.Fatalf("Event expected: %s, got: %s", exp, event.Op)
}
case <-time.After(100 * time.Millisecond):
t.Fatalf("Expected %s event not received", exp)
}
}
// Remove the (opened) file, check Chmod event (notifying
// about file link count change) is received
err = os.Remove(testFile)
if err != nil {
t.Fatalf("Failed to remove file: %s", err)
}
checkEvent(Chmod)
// Close the file, check Remove event is received
fd.Close()
checkEvent(Remove)
}

@shogo82148 shogo82148 merged commit 11c742d into main Mar 6, 2024
110 checks passed
@shogo82148 shogo82148 deleted the port-260 branch March 6, 2024 14:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

1 participant