From 55697a2121f8a489afe7f2992fe8b45c05826858 Mon Sep 17 00:00:00 2001 From: sjh9714 <163989462+sjh9714@users.noreply.github.com> Date: Mon, 6 Jul 2026 04:19:12 +0900 Subject: [PATCH] fix sqlite permission error flags --- pkg/analyze/sqlite.go | 23 +++++++++++------- pkg/analyze/sqlite_test.go | 50 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 62 insertions(+), 11 deletions(-) diff --git a/pkg/analyze/sqlite.go b/pkg/analyze/sqlite.go index 343614bd8..7e3c29fff 100644 --- a/pkg/analyze/sqlite.go +++ b/pkg/analyze/sqlite.go @@ -134,7 +134,7 @@ func (s *SqliteStorage) BeginBulkInsert() error { } s.updateStmt, err = tx.Prepare( - `UPDATE items SET size = ?, usage = ?, item_count = ? WHERE id = ?`, + `UPDATE items SET size = ?, usage = ?, item_count = ?, flag = ? WHERE id = ?`, ) if err != nil { s.insertStmt.Close() @@ -281,17 +281,17 @@ func (s *SqliteStorage) InsertItem( } // UpdateItem updates an existing item's stats -func (s *SqliteStorage) UpdateItem(id, size, usage, itemCount int64) error { +func (s *SqliteStorage) UpdateItem(id, size, usage, itemCount int64, flag rune) error { var err error // Use prepared statement if in bulk mode, otherwise use direct exec if s.updateStmt != nil { - _, err = s.updateStmt.Exec(size, usage, itemCount, id) + _, err = s.updateStmt.Exec(size, usage, itemCount, string(flag), id) } else { s.m.Lock() _, err = s.db.Exec( - `UPDATE items SET size = ?, usage = ?, item_count = ? WHERE id = ?`, - size, usage, itemCount, id, + `UPDATE items SET size = ?, usage = ?, item_count = ?, flag = ? WHERE id = ?`, + size, usage, itemCount, string(flag), id, ) s.m.Unlock() } @@ -853,11 +853,10 @@ func (a *SqliteAnalyzer) insertItemLocked( return a.storage.InsertItem(parentID, name, isDir, size, usage, mtime, itemCount, mli, flag) } -// updateItemLocked is a serialized wrapper around storage.UpdateItem. -func (a *SqliteAnalyzer) updateItemLocked(id, size, usage, itemCount int64) error { +func (a *SqliteAnalyzer) updateDirLocked(id, size, usage, itemCount int64, flag rune) error { a.dbWriteMu.Lock() defer a.dbWriteMu.Unlock() - return a.storage.UpdateItem(id, size, usage, itemCount) + return a.storage.UpdateItem(id, size, usage, itemCount, flag) } // hasInodeLocked is a serialized wrapper around storage.HasInode. @@ -1182,11 +1181,17 @@ func (a *SqliteAnalyzer) processDir(path string, parentID *int64) *SqliteItem { totalSize += sub.size totalUsage += sub.usage itemCount += sub.itemCount + switch sub.flag { + case '!', '.': + if dirFlag != '!' { + dirFlag = '.' + } + } } } // Update directory with computed stats - if err := a.updateItemLocked(dirID, totalSize, totalUsage, itemCount); err != nil { + if err := a.updateDirLocked(dirID, totalSize, totalUsage, itemCount, dirFlag); err != nil { log.Printf("Error updating item: %v", err) } diff --git a/pkg/analyze/sqlite_test.go b/pkg/analyze/sqlite_test.go index c1b4ac520..38403f8e1 100644 --- a/pkg/analyze/sqlite_test.go +++ b/pkg/analyze/sqlite_test.go @@ -7,6 +7,7 @@ import ( "os" "os/exec" "path/filepath" + "runtime" "slices" "testing" "time" @@ -186,7 +187,7 @@ func TestSqliteStorageUpdateItem(t *testing.T) { assert.NoError(t, err) // Update item - err = storage.UpdateItem(id, 500, 1000, 10) + err = storage.UpdateItem(id, 500, 1000, 10, ' ') assert.NoError(t, err) // Verify update @@ -217,7 +218,7 @@ func TestSqliteStorageBulkInsert(t *testing.T) { } // Update during bulk mode - err = storage.UpdateItem(rootID, 10000, 20000, 101) + err = storage.UpdateItem(rootID, 10000, 20000, 101, ' ') assert.NoError(t, err) // End bulk insert @@ -787,6 +788,51 @@ func TestSqliteAnalyzerAnalyzeDir(t *testing.T) { assert.Equal(t, int64(5), subnestedFiles[0].GetSize()) } +func TestSqliteAnalyzerPropagatesChildPermissionError(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("chmod does not make directories unreadable on Windows") + } + + rootPath := t.TempDir() + outerPath := filepath.Join(rootPath, "outer") + restrictedPath := filepath.Join(outerPath, "data") + + err := os.MkdirAll(restrictedPath, 0o755) + assert.NoError(t, err) + err = os.WriteFile(filepath.Join(restrictedPath, "file.txt"), []byte("x"), 0o600) + assert.NoError(t, err) + + err = os.Chmod(restrictedPath, 0) + assert.NoError(t, err) + defer os.Chmod(restrictedPath, 0o700) + + if _, err := os.ReadDir(restrictedPath); err == nil { + t.Skip("test user can still read chmod 000 directory") + } + + dbPath := filepath.Join(t.TempDir(), "test.db") + analyzer, err := CreateSqliteAnalyzer(dbPath) + assert.NoError(t, err) + defer analyzer.storage.Close() + + dir := analyzer.AnalyzeDir( + rootPath, func(_, _ string) bool { return false }, func(_ string) bool { return false }, + ).(*SqliteItem) + + analyzer.GetDone().Wait() + + files := slices.Collect(dir.GetFiles(fs.SortByName, fs.SortAsc)) + assert.Len(t, files, 1) + outer := files[0].(*SqliteItem) + assert.Equal(t, "outer", outer.GetName()) + assert.Equal(t, '.', outer.GetFlag()) + + children := slices.Collect(outer.GetFiles(fs.SortByName, fs.SortAsc)) + assert.Len(t, children, 1) + assert.Equal(t, "data", children[0].GetName()) + assert.Equal(t, '!', children[0].GetFlag()) +} + func TestSqliteAnalyzerIgnoreDir(t *testing.T) { fin := testdir.CreateTestDir() defer fin()