diff --git a/cmd/stfs/cmd/serve_ftp.go b/cmd/stfs/cmd/serve_ftp.go index 05475b0..4100c40 100644 --- a/cmd/stfs/cmd/serve_ftp.go +++ b/cmd/stfs/cmd/serve_ftp.go @@ -193,7 +193,7 @@ var serveFTPCmd = &cobra.Command{ ) }, viper.GetBool(readOnlyFlag), - true, // FTP requires read permission even if `O_WRONLY` is set if cache is enabled + true, // FTP requires read permission even if `O_WRONLY` is set if cache is enabled, as the cache needs to read the written file func(hdr *config.Header) { jsonLogger.Trace("Header transform", hdr) diff --git a/pkg/fs/filesystem.go b/pkg/fs/filesystem.go index 6efb8cc..07e1c03 100644 --- a/pkg/fs/filesystem.go +++ b/pkg/fs/filesystem.go @@ -31,10 +31,10 @@ type STFS struct { metadata config.MetadataConfig - compressionLevel string - getFileBuffer func() (cache.WriteCache, func() error, error) - readOnly bool - writeImpliesRead bool + compressionLevel string + getFileBuffer func() (cache.WriteCache, func() error, error) + readOnly bool + writePermImpliesReadPerm bool ioLock sync.Mutex @@ -51,7 +51,7 @@ func NewSTFS( compressionLevel string, getFileBuffer func() (cache.WriteCache, func() error, error), readOnly bool, - writeImpliesRead bool, + writePermImpliesReadPerm bool, onHeader func(hdr *config.Header), log logging.StructuredLogger, @@ -62,10 +62,10 @@ func NewSTFS( metadata: metadata, - compressionLevel: compressionLevel, - getFileBuffer: getFileBuffer, - readOnly: readOnly, - writeImpliesRead: writeImpliesRead, + compressionLevel: compressionLevel, + getFileBuffer: getFileBuffer, + readOnly: readOnly, + writePermImpliesReadPerm: writePermImpliesReadPerm, onHeader: onHeader, log: log, @@ -376,7 +376,7 @@ func (f *STFS) OpenFile(name string, flag int, perm os.FileMode) (afero.File, er if (flag & O_ACCMODE) == os.O_RDONLY { flags.Read = true } else if (flag & O_ACCMODE) == os.O_WRONLY { - if f.writeImpliesRead { + if f.writePermImpliesReadPerm { flags.Read = true } @@ -406,6 +406,21 @@ func (f *STFS) OpenFile(name string, flag int, perm os.FileMode) (afero.File, er if err != nil { if err == sql.ErrNoRows { if !f.readOnly && flag&os.O_CREATE != 0 && flag&os.O_EXCL == 0 { + if _, err := inventory.Stat( + f.metadata, + + filepath.Dir(name), + false, + + f.onHeader, + ); err != nil { + if err == sql.ErrNoRows { + return nil, os.ErrNotExist + } + + return nil, err + } + if err := f.mknodeWithoutLocking(false, name, perm, false, "", false); err != nil { return nil, err } diff --git a/pkg/fs/filesystem_test.go b/pkg/fs/filesystem_test.go index 8d3457a..833c40d 100644 --- a/pkg/fs/filesystem_test.go +++ b/pkg/fs/filesystem_test.go @@ -1114,15 +1114,14 @@ var openFileTests = []struct { func(f afero.File) error { return nil }, false, }, - // FIXME: STFS can create file in non-existent directory, which should not be possible - // { - // "Can not open /mydir/test.txt if O_CREATE is set", - // openFileArgs{"/mydir/test.txt", os.O_CREATE, os.ModePerm}, - // true, - // func(f afero.Fs) error { return nil }, - // func(f afero.File) error { return nil }, - // false, - // }, + { + "Can not open /mydir/test.txt if O_CREATE is set", + openFileArgs{"/mydir/test.txt", os.O_CREATE, os.ModePerm}, + true, + func(f afero.Fs) error { return nil }, + func(f afero.File) error { return nil }, + false, + }, { "Can open /mydir/test.txt after creating it", openFileArgs{"/mydir/test.txt", os.O_RDONLY, 0},