This repository has been archived by the owner on Oct 22, 2021. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 3
/
readdir_unix.go
155 lines (136 loc) · 3.27 KB
/
readdir_unix.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
package fs
import (
"context"
"golang.org/x/sys/unix"
"os"
"path"
"path/filepath"
typ "github.com/beyondstorage/go-storage/v4/types"
)
// Available value for Dirent Type
//
// Copied from linux kernel <dirent.h>
// #define DT_UNKNOWN 0
// #define DT_FIFO 1
// #define DT_CHR 2
// #define DT_DIR 4
// #define DT_BLK 6
// #define DT_REG 8
// #define DT_LNK 10
// #define DT_SOCK 12
// #define DT_WHT 14
const (
// The file type is unknown.
DirentTypeUnknown = 0
// This is a named pipe (FIFO).
DirentTypeFIFO = 1
// This is a character device.
DirentTypeCharDevice = 2
// This is a directory.
DirentTypeDirectory = 4
// This is a block device.
DirentTypeBlockDevice = 6
// This is a regular file.
DirentTypeRegular = 8
// This is a symbolic link.
DirentTypeLink = 10
// This is a UNIX domain socket.
DirentTypeSocket = 12
// WhiteOut from BSD, don't know what's it mean.
DirentTypeWhiteOut = 14
)
func (s *Storage) listDirNext(ctx context.Context, page *typ.ObjectPage) (err error) {
input := page.Status.(*listDirInput)
defer func() {
err = s.formatError("list_dir_next", err, input.rp)
}()
defer func() {
// Make sure file has been close every time we return an error
if err != nil && input.f != nil {
_ = input.f.Close()
input.f = nil
}
}()
// Open dir before we read it.
if input.f == nil {
input.f, err = os.Open(input.rp)
if err != nil {
return
}
}
// Reset bufp before refill buf.
input.bufp = 0
n, err := unix.ReadDirent(int(input.f.Fd()), *input.buf)
if err != nil {
return err
}
if n <= 0 {
return typ.IterateDone
}
for input.bufp < n {
// Drain the buffer
buf := (*input.buf)[input.bufp:n]
// Get and check reclen
reclen, ok := direntReclen(buf)
if !ok || reclen > uint64(len(buf)) {
return
}
input.bufp += int(reclen)
// current dirent
rec := buf[:reclen]
// Get and check inode
ino, ok := direntIno(rec)
if !ok {
break
}
if ino == 0 { // File absent in directory.
continue
}
// Get and check type
ty, ok := direntType(rec)
if !ok {
continue
}
// Get and check name
name := rec[direntOffsetName:reclen]
for i, c := range name {
if c == 0 {
name = name[:i]
break
}
}
// Format object
fname := string(name)
// Check for useless names before allocating a string.
if fname == "." || fname == ".." {
continue
}
if !input.started {
if fname != input.continuationToken {
continue
}
// ContinuationToken is the next file, we should include this file.
input.started = true
}
o := s.newObject(false)
// FIXME: filepath.Join and path.Join is really slow here, we need handle this.
// Always keep service original name as ID.
o.ID = filepath.Join(input.rp, fname)
// Object's name should always be separated by slash (/)
o.Path = path.Join(input.dir, fname)
switch ty {
case DirentTypeDirectory:
o.Mode |= typ.ModeDir
case DirentTypeRegular:
o.Mode |= typ.ModeRead | typ.ModeAppend | typ.ModePage
case DirentTypeLink:
o.Mode |= typ.ModeLink
}
// Set update name here.
input.continuationToken = o.Path
page.Data = append(page.Data, o)
}
return
}