From 9be040dd141cf11593262da8fa1ece5f28da10e5 Mon Sep 17 00:00:00 2001 From: Anis Elleuch Date: Sat, 10 Jul 2021 00:20:51 +0100 Subject: [PATCH] Add option in readDir to enable symlink following of dirs (#12668) --- cmd/fs-v1.go | 2 +- cmd/os-readdir-common.go | 36 ++++++++++++++++++++++++++++++++++++ cmd/os-readdir_other.go | 19 +++++++------------ cmd/os-readdir_unix.go | 11 ++++------- cmd/os-readdir_windows.go | 12 ++++-------- 5 files changed, 52 insertions(+), 28 deletions(-) create mode 100644 cmd/os-readdir-common.go diff --git a/cmd/fs-v1.go b/cmd/fs-v1.go index d295f57eb..ba6ad49a4 100644 --- a/cmd/fs-v1.go +++ b/cmd/fs-v1.go @@ -534,7 +534,7 @@ func (fs *FSObjects) ListBuckets(ctx context.Context) ([]BucketInfo, error) { atomic.AddInt64(&fs.activeIOCount, -1) }() - entries, err := readDir(fs.fsPath) + entries, err := readDirWithOpts(fs.fsPath, readDirOpts{count: -1, followDirSymlink: true}) if err != nil { logger.LogIf(ctx, errDiskNotFound) return nil, toObjectErr(errDiskNotFound) diff --git a/cmd/os-readdir-common.go b/cmd/os-readdir-common.go new file mode 100644 index 000000000..0caca9057 --- /dev/null +++ b/cmd/os-readdir-common.go @@ -0,0 +1,36 @@ +// Copyright (c) 2015-2021 MinIO, Inc. +// +// This file is part of MinIO Object Storage stack +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package cmd + +// Options for readDir function call +type readDirOpts struct { + // The maximum number of entries to return + count int + // Follow directory symlink + followDirSymlink bool +} + +// Return all the entries at the directory dirPath. +func readDir(dirPath string) (entries []string, err error) { + return readDirWithOpts(dirPath, readDirOpts{count: -1}) +} + +// Return up to count entries at the directory dirPath. +func readDirN(dirPath string, count int) (entries []string, err error) { + return readDirWithOpts(dirPath, readDirOpts{count: count}) +} diff --git a/cmd/os-readdir_other.go b/cmd/os-readdir_other.go index 66019e6f3..ed97c4627 100644 --- a/cmd/os-readdir_other.go +++ b/cmd/os-readdir_other.go @@ -30,11 +30,6 @@ func access(name string) error { return err } -// Return all the entries at the directory dirPath. -func readDir(dirPath string) (entries []string, err error) { - return readDirN(dirPath, -1) -} - // readDirFn applies the fn() function on each entries at dirPath, doesn't recurse into // the directory itself, if the dirPath doesn't exist this function doesn't return // an error. @@ -90,8 +85,8 @@ func readDirFn(dirPath string, filter func(name string, typ os.FileMode) error) return nil } -// Return N entries at the directory dirPath. If count is -1, return all entries -func readDirN(dirPath string, count int) (entries []string, err error) { +// Return entries at the directory dirPath. +func readDirWithOpts(dirPath string, opts readDirOpts) (entries []string, err error) { d, err := Open(dirPath) if err != nil { return nil, osErrToFileErr(err) @@ -99,12 +94,12 @@ func readDirN(dirPath string, count int) (entries []string, err error) { defer d.Close() maxEntries := 1000 - if count > 0 && count < maxEntries { + if opts.count > 0 && opts.count < maxEntries { maxEntries = count } done := false - remaining := count + remaining := opts.count for !done { // Read up to max number of entries. @@ -115,7 +110,7 @@ func readDirN(dirPath string, count int) (entries []string, err error) { } return nil, osErrToFileErr(err) } - if count > -1 { + if opts.count > -1 { if remaining <= len(fis) { fis = fis[:remaining] done = true @@ -136,7 +131,7 @@ func readDirN(dirPath string, count int) (entries []string, err error) { } // Ignore symlinked directories. - if fi.IsDir() { + if !opts.followDirSymlink && fi.IsDir() { continue } } @@ -147,7 +142,7 @@ func readDirN(dirPath string, count int) (entries []string, err error) { } else if fi.Mode().IsRegular() { entries = append(entries, fi.Name()) } - if count > 0 { + if opts.count > 0 { remaining-- } } diff --git a/cmd/os-readdir_unix.go b/cmd/os-readdir_unix.go index 7f24734e2..dd4f7dc1a 100644 --- a/cmd/os-readdir_unix.go +++ b/cmd/os-readdir_unix.go @@ -98,11 +98,6 @@ func parseDirEnt(buf []byte) (consumed int, name []byte, typ os.FileMode, err er return consumed, nameBuf[:nameLen], typ, nil } -// Return all the entries at the directory dirPath. -func readDir(dirPath string) (entries []string, err error) { - return readDirN(dirPath, -1) -} - // readDirFn applies the fn() function on each entries at dirPath, doesn't recurse into // the directory itself, if the dirPath doesn't exist this function doesn't return // an error. @@ -184,7 +179,7 @@ func readDirFn(dirPath string, fn func(name string, typ os.FileMode) error) erro // Return count entries at the directory dirPath and all entries // if count is set to -1 -func readDirN(dirPath string, count int) (entries []string, err error) { +func readDirWithOpts(dirPath string, opts readDirOpts) (entries []string, err error) { f, err := Open(dirPath) if err != nil { return nil, osErrToFileErr(err) @@ -202,6 +197,8 @@ func readDirN(dirPath string, count int) (entries []string, err error) { boff := 0 // starting read position in buf nbuf := 0 // end valid data in buf + count := opts.count + for count != 0 { if boff >= nbuf { boff = 0 @@ -242,7 +239,7 @@ func readDirN(dirPath string, count int) (entries []string, err error) { } // Ignore symlinked directories. - if typ&os.ModeSymlink == os.ModeSymlink && fi.IsDir() { + if !opts.followDirSymlink && typ&os.ModeSymlink == os.ModeSymlink && fi.IsDir() { continue } diff --git a/cmd/os-readdir_windows.go b/cmd/os-readdir_windows.go index e8f0e00d8..09ed0b9d7 100644 --- a/cmd/os-readdir_windows.go +++ b/cmd/os-readdir_windows.go @@ -30,11 +30,6 @@ func access(name string) error { return err } -// Return all the entries at the directory dirPath. -func readDir(dirPath string) (entries []string, err error) { - return readDirN(dirPath, -1) -} - // readDirFn applies the fn() function on each entries at dirPath, doesn't recurse into // the directory itself, if the dirPath doesn't exist this function doesn't return // an error. @@ -118,8 +113,8 @@ func readDirFn(dirPath string, filter func(name string, typ os.FileMode) error) return nil } -// Return N entries at the directory dirPath. If count is -1, return all entries -func readDirN(dirPath string, count int) (entries []string, err error) { +// Return N entries at the directory dirPath. +func readDirWithOpts(dirPath string, opts readDirOpts) (entries []string, err error) { f, err := os.Open(dirPath) if err != nil { return nil, osErrToFileErr(err) @@ -138,6 +133,7 @@ func readDirN(dirPath string, count int) (entries []string, err error) { data := &syscall.Win32finddata{} handle := syscall.Handle(f.Fd()) + count := opts.count for count != 0 { e := syscall.FindNextFile(handle, data) if e != nil { @@ -172,7 +168,7 @@ func readDirN(dirPath string, count int) (entries []string, err error) { return nil, err } - if fi.IsDir() { + if !opts.followDirSymlink && fi.IsDir() { // directory symlinks are ignored. continue }