{"record":{"id":"681b4c6dbaf4cf98","repo":"rqlite/rqlite","slug":"failed-to-test-writing-to-directory-s-w","errorCode":null,"errorMessage":"failed to test writing to directory %s: %w","messagePattern":"failed to test writing to directory (.+?): %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"auto/file/file.go","lineNumber":66,"sourceCode":"\t}\n\n\t// Validate file parameter for path traversal attacks and directory separators\n\tcleanFile := filepath.Clean(name)\n\tif strings.Contains(name, string(filepath.Separator)) ||\n\t\tstrings.Contains(cleanFile, \"..\") ||\n\t\tfilepath.IsAbs(cleanFile) ||\n\t\tcleanFile != name {\n\t\treturn nil, fmt.Errorf(\"invalid file parameter: %s (must be a simple filename without path separators)\", name)\n\t}\n\n\t// Ensure the destination directory exists and is writable\n\tif err := os.MkdirAll(dir, 0755); err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to create directory %s: %w\", dir, err)\n\t}\n\ttouchPath := filepath.Join(dir, \".touch\")\n\tf, err := os.OpenFile(touchPath, os.O_CREATE|os.O_WRONLY, 0644)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to test writing to directory %s: %w\", dir, err)\n\t}\n\tf.Close()\n\tos.Remove(touchPath)\n\n\tc := &Client{\n\t\tdir:      dir,\n\t\tname:     name,\n\t\tmetaPath: filepath.Join(dir, \"METADATA.json\"),\n\t}\n\n\tif opt != nil {\n\t\tc.timestamp = opt.Timestamp\n\t}\n\treturn c, nil\n}\n\n// CurrentMetadata returns the current metadata.\nfunc (c *Client) CurrentMetadata(ctx context.Context) (*Metadata, error) {","sourceCodeStart":48,"sourceCodeEnd":84,"githubUrl":"https://github.com/rqlite/rqlite/blob/7586a4d1bdbd9a5a80021664c5a863cd850adb60/auto/file/file.go#L48-L84","documentation":"After creating the directory, NewClient probes writability by creating and then removing a '.touch' file; failure to open the touch file produces this error. The directory exists but the process cannot write to it.","triggerScenarios":"os.OpenFile on <dir>/.touch fails because the process user lacks write permission on the directory, an immutable flag is set, or the write is blocked by security policy/SELinux/AppArmor.","commonSituations":"Directory owned by root but rqlited runs as the rqlite user; NFS mounts exported read-only or root-squashed; SELinux/AppArmor denials; backups directory made read-only after an incident.","solutions":["Check the wrapped OS error (typically EACCES/EROFS).","Grant write permission to the running user: chmod u+w <dir> or chown the directory.","Check SELinux/AppArmor audit logs if unix permissions look correct.","Ensure the mount is read-write (mount | grep <dir>; remount rw if needed)."],"exampleFix":"# before\ndr-xr-xr-x rqlite:rqlite /var/lib/rqlite/backup\n# after\nchmod u+w /var/lib/rqlite/backup\n# verify\nsudo -u rqlite touch /var/lib/rqlite/backup/.probe && rm /var/lib/rqlite/backup/.probe","handlingStrategy":"validation","validationCode":"info, err := os.Stat(dir)\nif err != nil || !info.IsDir() { return fmt.Errorf(\"not a directory: %s\", dir) }\nif info.Mode().Perm()&0o200 == 0 { return fmt.Errorf(\"directory not writable by owner: %s\", dir) }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Ensure the running user has the write bit on the directory (chmod u+w).","Check SELinux/AppArmor denials in audit logs when unix perms look fine.","Beware root-squashed/read-only NFS exports for backup directories.","Include a writable-directory check in service startup probes."],"tags":["filesystem","permissions","write","directory"],"backgroundTag":"directory-not-writable","analyzedSha":"7586a4d1bdbd9a5a80021664c5a863cd850adb60","analyzedAt":"2026-09-03T07:03:02.260Z","contentChangedAt":"2026-09-03T07:03:02.260Z","schemaVersion":2},"datasetVersion":"2026-09-10T12:17:11.382Z"}