mirror of
https://github.com/minio/minio.git
synced 2024-12-24 06:05:55 -05:00
fix: Bring support for symlink on regular files on NAS (#11383)
fixes #11203
This commit is contained in:
parent
85d2187c20
commit
8cad407e0b
@ -56,6 +56,24 @@ func readDirFn(dirPath string, filter func(name string, typ os.FileMode) error)
|
||||
return osErrToFileErr(err)
|
||||
}
|
||||
for _, fi := range fis {
|
||||
if fi.Mode()&os.ModeSymlink == os.ModeSymlink {
|
||||
fi, err = os.Stat(pathJoin(dirPath, fi.Name()))
|
||||
if err != nil {
|
||||
// It got deleted in the meantime, not found
|
||||
// or returns too many symlinks ignore this
|
||||
// file/directory.
|
||||
if osIsNotExist(err) || isSysErrPathNotFound(err) ||
|
||||
isSysErrTooManySymlinks(err) {
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Ignore symlinked directories.
|
||||
if fi.IsDir() {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if err = filter(fi.Name(), fi.Mode()); err == errDoneForNow {
|
||||
// filtering requested to return by caller.
|
||||
return nil
|
||||
@ -97,11 +115,26 @@ func readDirN(dirPath string, count int) (entries []string, err error) {
|
||||
}
|
||||
}
|
||||
for _, fi := range fis {
|
||||
// Not need to follow symlink.
|
||||
if fi.Mode()&os.ModeSymlink == os.ModeSymlink {
|
||||
continue
|
||||
fi, err = os.Stat(pathJoin(dirPath, fi.Name()))
|
||||
if err != nil {
|
||||
// It got deleted in the meantime, not found
|
||||
// or returns too many symlinks ignore this
|
||||
// file/directory.
|
||||
if osIsNotExist(err) || isSysErrPathNotFound(err) ||
|
||||
isSysErrTooManySymlinks(err) {
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Ignore symlinked directories.
|
||||
if fi.IsDir() {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if fi.Mode().IsDir() {
|
||||
|
||||
if fi.IsDir() {
|
||||
// Append SlashSeparator instead of "\" so that sorting is achieved as expected.
|
||||
entries = append(entries, fi.Name()+SlashSeparator)
|
||||
} else if fi.Mode().IsRegular() {
|
||||
|
@ -149,7 +149,8 @@ func setupTestReadDirSymlink(t *testing.T) (testResults []result) {
|
||||
}
|
||||
// Add to entries.
|
||||
entries = append(entries, name1)
|
||||
// Symlinks are ignored.
|
||||
// Symlinks are preserved for regular files
|
||||
entries = append(entries, name2)
|
||||
}
|
||||
if err := os.MkdirAll(filepath.Join(dir, "mydir"), 0777); err != nil {
|
||||
t.Fatalf("Unable to create \"mydir\", %s", err)
|
||||
|
@ -123,8 +123,29 @@ func readDirFn(dirPath string, fn func(name string, typ os.FileMode) error) erro
|
||||
if len(name) == 0 || bytes.Equal(name, []byte{'.'}) || bytes.Equal(name, []byte{'.', '.'}) {
|
||||
continue
|
||||
}
|
||||
if typ&os.ModeSymlink == os.ModeSymlink {
|
||||
continue
|
||||
|
||||
// Fallback for filesystems (like old XFS) that don't
|
||||
// support Dirent.Type and have DT_UNKNOWN (0) there
|
||||
// instead.
|
||||
if typ == unexpectedFileMode || typ&os.ModeSymlink == os.ModeSymlink {
|
||||
fi, err := os.Stat(pathJoin(dirPath, string(name)))
|
||||
if err != nil {
|
||||
// It got deleted in the meantime, not found
|
||||
// or returns too many symlinks ignore this
|
||||
// file/directory.
|
||||
if osIsNotExist(err) || isSysErrPathNotFound(err) ||
|
||||
isSysErrTooManySymlinks(err) {
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Ignore symlinked directories.
|
||||
if typ&os.ModeSymlink == os.ModeSymlink && fi.IsDir() {
|
||||
continue
|
||||
}
|
||||
|
||||
typ = fi.Mode() & os.ModeType
|
||||
}
|
||||
if err = fn(string(name), typ); err == errDoneForNow {
|
||||
// fn() requested to return by caller.
|
||||
@ -176,11 +197,12 @@ func readDirN(dirPath string, count int) (entries []string, err error) {
|
||||
if len(name) == 0 || bytes.Equal(name, []byte{'.'}) || bytes.Equal(name, []byte{'.', '.'}) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Fallback for filesystems (like old XFS) that don't
|
||||
// support Dirent.Type and have DT_UNKNOWN (0) there
|
||||
// instead.
|
||||
if typ == unexpectedFileMode {
|
||||
fi, err := os.Lstat(pathJoin(dirPath, string(name)))
|
||||
if typ == unexpectedFileMode || typ&os.ModeSymlink == os.ModeSymlink {
|
||||
fi, err := os.Stat(pathJoin(dirPath, string(name)))
|
||||
if err != nil {
|
||||
// It got deleted in the meantime, not found
|
||||
// or returns too many symlinks ignore this
|
||||
@ -191,22 +213,30 @@ func readDirN(dirPath string, count int) (entries []string, err error) {
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Ignore symlinked directories.
|
||||
if typ&os.ModeSymlink == os.ModeSymlink && fi.IsDir() {
|
||||
continue
|
||||
}
|
||||
|
||||
typ = fi.Mode() & os.ModeType
|
||||
}
|
||||
if typ&os.ModeSymlink == os.ModeSymlink {
|
||||
continue
|
||||
}
|
||||
|
||||
var nameStr string
|
||||
if typ.IsRegular() {
|
||||
entries = append(entries, string(name))
|
||||
nameStr = string(name)
|
||||
} else if typ.IsDir() {
|
||||
// Use temp buffer to append a slash to avoid string concat.
|
||||
tmp = tmp[:len(name)+1]
|
||||
copy(tmp, name)
|
||||
tmp[len(tmp)-1] = '/' // SlashSeparator
|
||||
entries = append(entries, string(tmp))
|
||||
nameStr = string(tmp)
|
||||
}
|
||||
|
||||
count--
|
||||
entries = append(entries, nameStr)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -42,6 +42,15 @@ func readDirFn(dirPath string, filter func(name string, typ os.FileMode) error)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
// Check if file or dir. This is the quickest way.
|
||||
// Do not remove this check, on windows syscall.FindNextFile
|
||||
// would throw an exception if Fd() points to a file
|
||||
// instead of a directory, we need to quickly fail
|
||||
// in such situations - this workadound is expected.
|
||||
if _, err = f.Seek(0, io.SeekStart); err == nil {
|
||||
return errFileNotFound
|
||||
}
|
||||
|
||||
data := &syscall.Win32finddata{}
|
||||
for {
|
||||
e := syscall.FindNextFile(syscall.Handle(f.Fd()), data)
|
||||
@ -63,13 +72,33 @@ func readDirFn(dirPath string, filter func(name string, typ os.FileMode) error)
|
||||
if name == "" || name == "." || name == ".." { // Useless names
|
||||
continue
|
||||
}
|
||||
if data.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT != 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
var typ os.FileMode = 0 // regular file
|
||||
if data.FileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 {
|
||||
switch {
|
||||
case data.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT != 0:
|
||||
// Reparse point is a symlink
|
||||
fi, err := os.Stat(pathJoin(dirPath, string(name)))
|
||||
if err != nil {
|
||||
// It got deleted in the meantime, not found
|
||||
// or returns too many symlinks ignore this
|
||||
// file/directory.
|
||||
if osIsNotExist(err) || isSysErrPathNotFound(err) ||
|
||||
isSysErrTooManySymlinks(err) {
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
if fi.IsDir() {
|
||||
// Ignore symlinked directories.
|
||||
continue
|
||||
}
|
||||
|
||||
typ = fi.Mode()
|
||||
case data.FileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY != 0:
|
||||
typ = os.ModeDir
|
||||
}
|
||||
|
||||
if e = filter(name, typ); e == errDoneForNow {
|
||||
// filtering requested to return by caller.
|
||||
return nil
|
||||
@ -88,10 +117,14 @@ func readDirN(dirPath string, count int) (entries []string, err error) {
|
||||
defer f.Close()
|
||||
|
||||
// Check if file or dir. This is the quickest way.
|
||||
_, err = f.Seek(0, io.SeekStart)
|
||||
if err == nil {
|
||||
// Do not remove this check, on windows syscall.FindNextFile
|
||||
// would throw an exception if Fd() points to a file
|
||||
// instead of a directory, we need to quickly fail
|
||||
// in such situations - this workadound is expected.
|
||||
if _, err = f.Seek(0, io.SeekStart); err == nil {
|
||||
return nil, errFileNotFound
|
||||
}
|
||||
|
||||
data := &syscall.Win32finddata{}
|
||||
handle := syscall.Handle(f.Fd())
|
||||
|
||||
@ -113,15 +146,33 @@ func readDirN(dirPath string, count int) (entries []string, err error) {
|
||||
if name == "" || name == "." || name == ".." { // Useless names
|
||||
continue
|
||||
}
|
||||
|
||||
switch {
|
||||
case data.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT != 0:
|
||||
continue
|
||||
// Reparse point is a symlink
|
||||
fi, err := os.Stat(pathJoin(dirPath, string(name)))
|
||||
if err != nil {
|
||||
// It got deleted in the meantime, not found
|
||||
// or returns too many symlinks ignore this
|
||||
// file/directory.
|
||||
if osIsNotExist(err) || isSysErrPathNotFound(err) ||
|
||||
isSysErrTooManySymlinks(err) {
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if fi.IsDir() {
|
||||
// directory symlinks are ignored.
|
||||
continue
|
||||
}
|
||||
case data.FileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY != 0:
|
||||
entries = append(entries, name+SlashSeparator)
|
||||
default:
|
||||
entries = append(entries, name)
|
||||
name = name + SlashSeparator
|
||||
}
|
||||
|
||||
count--
|
||||
entries = append(entries, name)
|
||||
|
||||
}
|
||||
|
||||
return entries, nil
|
||||
|
@ -79,6 +79,23 @@ export MINIO_NOTIFY_WEBHOOK_QUEUE_DIR_1=/tmp/webhk
|
||||
|
||||
> NOTE: Please check the docs for the corresponding ENV setting. Alternatively, We can obtain other ENVs in the form `mc admin config set alias/ <sub-sys> --env`
|
||||
|
||||
## Symlink support
|
||||
|
||||
NAS gateway implementation allows symlinks on regular files,
|
||||
|
||||
### Behavior
|
||||
|
||||
- For reads symlink resolves to file symlink points to.
|
||||
- For deletes
|
||||
- Delete of symlink deletes the symlink but not the real file to which the symlink points.
|
||||
- Delete of actual file automatically makes symlink'ed file invisible, dangling symlinks won't be visible.
|
||||
|
||||
#### Caveats
|
||||
- Disallows follow of directory symlinks to avoid security issues, and leaving them as is on namespace makes them very inconsistent.
|
||||
- Dangling symlinks are ignored automatically.
|
||||
|
||||
*Directory symlinks is not and will not be supported as there are no safe ways to handle them.*
|
||||
|
||||
## Explore Further
|
||||
- [`mc` command-line interface](https://docs.min.io/docs/minio-client-quickstart-guide)
|
||||
- [`aws` command-line interface](https://docs.min.io/docs/aws-cli-with-minio)
|
||||
|
Loading…
Reference in New Issue
Block a user