You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

629 lines
16 KiB

5 years ago
5 years ago
4 years ago
4 years ago
5 years ago
3 years ago
3 years ago
more solid weed mount (#4089) * compare chunks by timestamp * fix slab clearing error * fix test compilation * move oldest chunk to sealed, instead of by fullness * lock on fh.entryViewCache * remove verbose logs * revert slat clearing * less logs * less logs * track write and read by timestamp * remove useless logic * add entry lock on file handle release * use mem chunk only, swap file chunk has problems * comment out code that maybe used later * add debug mode to compare data read and write * more efficient readResolvedChunks with linked list * small optimization * fix test compilation * minor fix on writer * add SeparateGarbageChunks * group chunks into sections * turn off debug mode * fix tests * fix tests * tmp enable swap file chunk * Revert "tmp enable swap file chunk" This reverts commit 985137ec472924e4815f258189f6ca9f2168a0a7. * simple refactoring * simple refactoring * do not re-use swap file chunk. Sealed chunks should not be re-used. * comment out debugging facilities * either mem chunk or swap file chunk is fine now * remove orderedMutex as *semaphore.Weighted not found impactful * optimize size calculation for changing large files * optimize performance to avoid going through the long list of chunks * still problems with swap file chunk * rename * tiny optimization * swap file chunk save only successfully read data * fix * enable both mem and swap file chunk * resolve chunks with range * rename * fix chunk interval list * also change file handle chunk group when adding chunks * pick in-active chunk with time-decayed counter * fix compilation * avoid nil with empty fh.entry * refactoring * rename * rename * refactor visible intervals to *list.List * refactor chunkViews to *list.List * add IntervalList for generic interval list * change visible interval to use IntervalList in generics * cahnge chunkViews to *IntervalList[*ChunkView] * use NewFileChunkSection to create * rename variables * refactor * fix renaming leftover * renaming * renaming * add insert interval * interval list adds lock * incrementally add chunks to readers Fixes: 1. set start and stop offset for the value object 2. clone the value object 3. use pointer instead of copy-by-value when passing to interval.Value 4. use insert interval since adding chunk could be out of order * fix tests compilation * fix tests compilation
2 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
more solid weed mount (#4089) * compare chunks by timestamp * fix slab clearing error * fix test compilation * move oldest chunk to sealed, instead of by fullness * lock on fh.entryViewCache * remove verbose logs * revert slat clearing * less logs * less logs * track write and read by timestamp * remove useless logic * add entry lock on file handle release * use mem chunk only, swap file chunk has problems * comment out code that maybe used later * add debug mode to compare data read and write * more efficient readResolvedChunks with linked list * small optimization * fix test compilation * minor fix on writer * add SeparateGarbageChunks * group chunks into sections * turn off debug mode * fix tests * fix tests * tmp enable swap file chunk * Revert "tmp enable swap file chunk" This reverts commit 985137ec472924e4815f258189f6ca9f2168a0a7. * simple refactoring * simple refactoring * do not re-use swap file chunk. Sealed chunks should not be re-used. * comment out debugging facilities * either mem chunk or swap file chunk is fine now * remove orderedMutex as *semaphore.Weighted not found impactful * optimize size calculation for changing large files * optimize performance to avoid going through the long list of chunks * still problems with swap file chunk * rename * tiny optimization * swap file chunk save only successfully read data * fix * enable both mem and swap file chunk * resolve chunks with range * rename * fix chunk interval list * also change file handle chunk group when adding chunks * pick in-active chunk with time-decayed counter * fix compilation * avoid nil with empty fh.entry * refactoring * rename * rename * refactor visible intervals to *list.List * refactor chunkViews to *list.List * add IntervalList for generic interval list * change visible interval to use IntervalList in generics * cahnge chunkViews to *IntervalList[*ChunkView] * use NewFileChunkSection to create * rename variables * refactor * fix renaming leftover * renaming * renaming * add insert interval * interval list adds lock * incrementally add chunks to readers Fixes: 1. set start and stop offset for the value object 2. clone the value object 3. use pointer instead of copy-by-value when passing to interval.Value 4. use insert interval since adding chunk could be out of order * fix tests compilation * fix tests compilation
2 years ago
more solid weed mount (#4089) * compare chunks by timestamp * fix slab clearing error * fix test compilation * move oldest chunk to sealed, instead of by fullness * lock on fh.entryViewCache * remove verbose logs * revert slat clearing * less logs * less logs * track write and read by timestamp * remove useless logic * add entry lock on file handle release * use mem chunk only, swap file chunk has problems * comment out code that maybe used later * add debug mode to compare data read and write * more efficient readResolvedChunks with linked list * small optimization * fix test compilation * minor fix on writer * add SeparateGarbageChunks * group chunks into sections * turn off debug mode * fix tests * fix tests * tmp enable swap file chunk * Revert "tmp enable swap file chunk" This reverts commit 985137ec472924e4815f258189f6ca9f2168a0a7. * simple refactoring * simple refactoring * do not re-use swap file chunk. Sealed chunks should not be re-used. * comment out debugging facilities * either mem chunk or swap file chunk is fine now * remove orderedMutex as *semaphore.Weighted not found impactful * optimize size calculation for changing large files * optimize performance to avoid going through the long list of chunks * still problems with swap file chunk * rename * tiny optimization * swap file chunk save only successfully read data * fix * enable both mem and swap file chunk * resolve chunks with range * rename * fix chunk interval list * also change file handle chunk group when adding chunks * pick in-active chunk with time-decayed counter * fix compilation * avoid nil with empty fh.entry * refactoring * rename * rename * refactor visible intervals to *list.List * refactor chunkViews to *list.List * add IntervalList for generic interval list * change visible interval to use IntervalList in generics * cahnge chunkViews to *IntervalList[*ChunkView] * use NewFileChunkSection to create * rename variables * refactor * fix renaming leftover * renaming * renaming * add insert interval * interval list adds lock * incrementally add chunks to readers Fixes: 1. set start and stop offset for the value object 2. clone the value object 3. use pointer instead of copy-by-value when passing to interval.Value 4. use insert interval since adding chunk could be out of order * fix tests compilation * fix tests compilation
2 years ago
more solid weed mount (#4089) * compare chunks by timestamp * fix slab clearing error * fix test compilation * move oldest chunk to sealed, instead of by fullness * lock on fh.entryViewCache * remove verbose logs * revert slat clearing * less logs * less logs * track write and read by timestamp * remove useless logic * add entry lock on file handle release * use mem chunk only, swap file chunk has problems * comment out code that maybe used later * add debug mode to compare data read and write * more efficient readResolvedChunks with linked list * small optimization * fix test compilation * minor fix on writer * add SeparateGarbageChunks * group chunks into sections * turn off debug mode * fix tests * fix tests * tmp enable swap file chunk * Revert "tmp enable swap file chunk" This reverts commit 985137ec472924e4815f258189f6ca9f2168a0a7. * simple refactoring * simple refactoring * do not re-use swap file chunk. Sealed chunks should not be re-used. * comment out debugging facilities * either mem chunk or swap file chunk is fine now * remove orderedMutex as *semaphore.Weighted not found impactful * optimize size calculation for changing large files * optimize performance to avoid going through the long list of chunks * still problems with swap file chunk * rename * tiny optimization * swap file chunk save only successfully read data * fix * enable both mem and swap file chunk * resolve chunks with range * rename * fix chunk interval list * also change file handle chunk group when adding chunks * pick in-active chunk with time-decayed counter * fix compilation * avoid nil with empty fh.entry * refactoring * rename * rename * refactor visible intervals to *list.List * refactor chunkViews to *list.List * add IntervalList for generic interval list * change visible interval to use IntervalList in generics * cahnge chunkViews to *IntervalList[*ChunkView] * use NewFileChunkSection to create * rename variables * refactor * fix renaming leftover * renaming * renaming * add insert interval * interval list adds lock * incrementally add chunks to readers Fixes: 1. set start and stop offset for the value object 2. clone the value object 3. use pointer instead of copy-by-value when passing to interval.Value 4. use insert interval since adding chunk could be out of order * fix tests compilation * fix tests compilation
2 years ago
4 years ago
5 years ago
5 years ago
more solid weed mount (#4089) * compare chunks by timestamp * fix slab clearing error * fix test compilation * move oldest chunk to sealed, instead of by fullness * lock on fh.entryViewCache * remove verbose logs * revert slat clearing * less logs * less logs * track write and read by timestamp * remove useless logic * add entry lock on file handle release * use mem chunk only, swap file chunk has problems * comment out code that maybe used later * add debug mode to compare data read and write * more efficient readResolvedChunks with linked list * small optimization * fix test compilation * minor fix on writer * add SeparateGarbageChunks * group chunks into sections * turn off debug mode * fix tests * fix tests * tmp enable swap file chunk * Revert "tmp enable swap file chunk" This reverts commit 985137ec472924e4815f258189f6ca9f2168a0a7. * simple refactoring * simple refactoring * do not re-use swap file chunk. Sealed chunks should not be re-used. * comment out debugging facilities * either mem chunk or swap file chunk is fine now * remove orderedMutex as *semaphore.Weighted not found impactful * optimize size calculation for changing large files * optimize performance to avoid going through the long list of chunks * still problems with swap file chunk * rename * tiny optimization * swap file chunk save only successfully read data * fix * enable both mem and swap file chunk * resolve chunks with range * rename * fix chunk interval list * also change file handle chunk group when adding chunks * pick in-active chunk with time-decayed counter * fix compilation * avoid nil with empty fh.entry * refactoring * rename * rename * refactor visible intervals to *list.List * refactor chunkViews to *list.List * add IntervalList for generic interval list * change visible interval to use IntervalList in generics * cahnge chunkViews to *IntervalList[*ChunkView] * use NewFileChunkSection to create * rename variables * refactor * fix renaming leftover * renaming * renaming * add insert interval * interval list adds lock * incrementally add chunks to readers Fixes: 1. set start and stop offset for the value object 2. clone the value object 3. use pointer instead of copy-by-value when passing to interval.Value 4. use insert interval since adding chunk could be out of order * fix tests compilation * fix tests compilation
2 years ago
5 years ago
5 years ago
more solid weed mount (#4089) * compare chunks by timestamp * fix slab clearing error * fix test compilation * move oldest chunk to sealed, instead of by fullness * lock on fh.entryViewCache * remove verbose logs * revert slat clearing * less logs * less logs * track write and read by timestamp * remove useless logic * add entry lock on file handle release * use mem chunk only, swap file chunk has problems * comment out code that maybe used later * add debug mode to compare data read and write * more efficient readResolvedChunks with linked list * small optimization * fix test compilation * minor fix on writer * add SeparateGarbageChunks * group chunks into sections * turn off debug mode * fix tests * fix tests * tmp enable swap file chunk * Revert "tmp enable swap file chunk" This reverts commit 985137ec472924e4815f258189f6ca9f2168a0a7. * simple refactoring * simple refactoring * do not re-use swap file chunk. Sealed chunks should not be re-used. * comment out debugging facilities * either mem chunk or swap file chunk is fine now * remove orderedMutex as *semaphore.Weighted not found impactful * optimize size calculation for changing large files * optimize performance to avoid going through the long list of chunks * still problems with swap file chunk * rename * tiny optimization * swap file chunk save only successfully read data * fix * enable both mem and swap file chunk * resolve chunks with range * rename * fix chunk interval list * also change file handle chunk group when adding chunks * pick in-active chunk with time-decayed counter * fix compilation * avoid nil with empty fh.entry * refactoring * rename * rename * refactor visible intervals to *list.List * refactor chunkViews to *list.List * add IntervalList for generic interval list * change visible interval to use IntervalList in generics * cahnge chunkViews to *IntervalList[*ChunkView] * use NewFileChunkSection to create * rename variables * refactor * fix renaming leftover * renaming * renaming * add insert interval * interval list adds lock * incrementally add chunks to readers Fixes: 1. set start and stop offset for the value object 2. clone the value object 3. use pointer instead of copy-by-value when passing to interval.Value 4. use insert interval since adding chunk could be out of order * fix tests compilation * fix tests compilation
2 years ago
more solid weed mount (#4089) * compare chunks by timestamp * fix slab clearing error * fix test compilation * move oldest chunk to sealed, instead of by fullness * lock on fh.entryViewCache * remove verbose logs * revert slat clearing * less logs * less logs * track write and read by timestamp * remove useless logic * add entry lock on file handle release * use mem chunk only, swap file chunk has problems * comment out code that maybe used later * add debug mode to compare data read and write * more efficient readResolvedChunks with linked list * small optimization * fix test compilation * minor fix on writer * add SeparateGarbageChunks * group chunks into sections * turn off debug mode * fix tests * fix tests * tmp enable swap file chunk * Revert "tmp enable swap file chunk" This reverts commit 985137ec472924e4815f258189f6ca9f2168a0a7. * simple refactoring * simple refactoring * do not re-use swap file chunk. Sealed chunks should not be re-used. * comment out debugging facilities * either mem chunk or swap file chunk is fine now * remove orderedMutex as *semaphore.Weighted not found impactful * optimize size calculation for changing large files * optimize performance to avoid going through the long list of chunks * still problems with swap file chunk * rename * tiny optimization * swap file chunk save only successfully read data * fix * enable both mem and swap file chunk * resolve chunks with range * rename * fix chunk interval list * also change file handle chunk group when adding chunks * pick in-active chunk with time-decayed counter * fix compilation * avoid nil with empty fh.entry * refactoring * rename * rename * refactor visible intervals to *list.List * refactor chunkViews to *list.List * add IntervalList for generic interval list * change visible interval to use IntervalList in generics * cahnge chunkViews to *IntervalList[*ChunkView] * use NewFileChunkSection to create * rename variables * refactor * fix renaming leftover * renaming * renaming * add insert interval * interval list adds lock * incrementally add chunks to readers Fixes: 1. set start and stop offset for the value object 2. clone the value object 3. use pointer instead of copy-by-value when passing to interval.Value 4. use insert interval since adding chunk could be out of order * fix tests compilation * fix tests compilation
2 years ago
5 years ago
5 years ago
5 years ago
3 years ago
3 years ago
5 years ago
5 years ago
  1. package weed_server
  2. import (
  3. "context"
  4. "fmt"
  5. "io"
  6. "os"
  7. "path"
  8. "strings"
  9. "time"
  10. "github.com/seaweedfs/seaweedfs/weed/util/buffered_writer"
  11. "golang.org/x/net/webdav"
  12. "google.golang.org/grpc"
  13. "github.com/seaweedfs/seaweedfs/weed/operation"
  14. "github.com/seaweedfs/seaweedfs/weed/pb"
  15. "github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
  16. "github.com/seaweedfs/seaweedfs/weed/util"
  17. "github.com/seaweedfs/seaweedfs/weed/util/chunk_cache"
  18. "github.com/seaweedfs/seaweedfs/weed/filer"
  19. "github.com/seaweedfs/seaweedfs/weed/glog"
  20. "github.com/seaweedfs/seaweedfs/weed/security"
  21. )
  22. type WebDavOption struct {
  23. Filer pb.ServerAddress
  24. FilerRootPath string
  25. DomainName string
  26. BucketsPath string
  27. GrpcDialOption grpc.DialOption
  28. Collection string
  29. Replication string
  30. DiskType string
  31. Uid uint32
  32. Gid uint32
  33. Cipher bool
  34. CacheDir string
  35. CacheSizeMB int64
  36. }
  37. type WebDavServer struct {
  38. option *WebDavOption
  39. secret security.SigningKey
  40. filer *filer.Filer
  41. grpcDialOption grpc.DialOption
  42. Handler *webdav.Handler
  43. }
  44. func max(x, y int64) int64 {
  45. if x <= y {
  46. return y
  47. }
  48. return x
  49. }
  50. func NewWebDavServer(option *WebDavOption) (ws *WebDavServer, err error) {
  51. fs, _ := NewWebDavFileSystem(option)
  52. // Fix no set filer.path , accessing "/" returns "//"
  53. if option.FilerRootPath == "/" {
  54. option.FilerRootPath = ""
  55. }
  56. // filer.path non "/" option means we are accessing filer's sub-folders
  57. if option.FilerRootPath != "" {
  58. fs = NewWrappedFs(fs, path.Clean(option.FilerRootPath))
  59. }
  60. ws = &WebDavServer{
  61. option: option,
  62. grpcDialOption: security.LoadClientTLS(util.GetViper(), "grpc.filer"),
  63. Handler: &webdav.Handler{
  64. FileSystem: fs,
  65. LockSystem: webdav.NewMemLS(),
  66. },
  67. }
  68. return ws, nil
  69. }
  70. // adapted from https://github.com/mattn/davfs/blob/master/plugin/mysql/mysql.go
  71. type WebDavFileSystem struct {
  72. option *WebDavOption
  73. secret security.SigningKey
  74. grpcDialOption grpc.DialOption
  75. chunkCache *chunk_cache.TieredChunkCache
  76. readerCache *filer.ReaderCache
  77. signature int32
  78. }
  79. type FileInfo struct {
  80. name string
  81. size int64
  82. mode os.FileMode
  83. modifiedTime time.Time
  84. etag string
  85. isDirectory bool
  86. }
  87. func (fi *FileInfo) Name() string { return fi.name }
  88. func (fi *FileInfo) Size() int64 { return fi.size }
  89. func (fi *FileInfo) Mode() os.FileMode { return fi.mode }
  90. func (fi *FileInfo) ModTime() time.Time { return fi.modifiedTime }
  91. func (fi *FileInfo) IsDir() bool { return fi.isDirectory }
  92. func (fi *FileInfo) Sys() interface{} { return nil }
  93. func (fi *FileInfo) ETag(ctx context.Context) (string, error) {
  94. return fi.etag, nil
  95. }
  96. type WebDavFile struct {
  97. fs *WebDavFileSystem
  98. name string
  99. isDirectory bool
  100. off int64
  101. entry *filer_pb.Entry
  102. visibleIntervals *filer.IntervalList[*filer.VisibleInterval]
  103. reader io.ReaderAt
  104. bufWriter *buffered_writer.BufferedWriteCloser
  105. }
  106. func NewWebDavFileSystem(option *WebDavOption) (webdav.FileSystem, error) {
  107. cacheUniqueId := util.Md5String([]byte("webdav" + string(option.Filer) + util.Version()))[0:8]
  108. cacheDir := path.Join(option.CacheDir, cacheUniqueId)
  109. os.MkdirAll(cacheDir, os.FileMode(0755))
  110. chunkCache := chunk_cache.NewTieredChunkCache(256, cacheDir, option.CacheSizeMB, 1024*1024)
  111. t := &WebDavFileSystem{
  112. option: option,
  113. chunkCache: chunkCache,
  114. signature: util.RandomInt32(),
  115. }
  116. t.readerCache = filer.NewReaderCache(32, chunkCache, filer.LookupFn(t))
  117. return t, nil
  118. }
  119. var _ = filer_pb.FilerClient(&WebDavFileSystem{})
  120. func (fs *WebDavFileSystem) WithFilerClient(streamingMode bool, fn func(filer_pb.SeaweedFilerClient) error) error {
  121. return pb.WithGrpcClient(streamingMode, fs.signature, func(grpcConnection *grpc.ClientConn) error {
  122. client := filer_pb.NewSeaweedFilerClient(grpcConnection)
  123. return fn(client)
  124. }, fs.option.Filer.ToGrpcAddress(), false, fs.option.GrpcDialOption)
  125. }
  126. func (fs *WebDavFileSystem) AdjustedUrl(location *filer_pb.Location) string {
  127. return location.Url
  128. }
  129. func (fs *WebDavFileSystem) GetDataCenter() string {
  130. return ""
  131. }
  132. func clearName(name string) (string, error) {
  133. slashed := strings.HasSuffix(name, "/")
  134. name = path.Clean(name)
  135. if !strings.HasSuffix(name, "/") && slashed {
  136. name += "/"
  137. }
  138. if !strings.HasPrefix(name, "/") {
  139. return "", os.ErrInvalid
  140. }
  141. return name, nil
  142. }
  143. func (fs *WebDavFileSystem) Mkdir(ctx context.Context, fullDirPath string, perm os.FileMode) error {
  144. glog.V(2).Infof("WebDavFileSystem.Mkdir %v", fullDirPath)
  145. if !strings.HasSuffix(fullDirPath, "/") {
  146. fullDirPath += "/"
  147. }
  148. var err error
  149. if fullDirPath, err = clearName(fullDirPath); err != nil {
  150. return err
  151. }
  152. _, err = fs.stat(ctx, fullDirPath)
  153. if err == nil {
  154. return os.ErrExist
  155. }
  156. return fs.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error {
  157. dir, name := util.FullPath(fullDirPath).DirAndName()
  158. request := &filer_pb.CreateEntryRequest{
  159. Directory: dir,
  160. Entry: &filer_pb.Entry{
  161. Name: name,
  162. IsDirectory: true,
  163. Attributes: &filer_pb.FuseAttributes{
  164. Mtime: time.Now().Unix(),
  165. Crtime: time.Now().Unix(),
  166. FileMode: uint32(perm | os.ModeDir),
  167. Uid: fs.option.Uid,
  168. Gid: fs.option.Gid,
  169. },
  170. },
  171. Signatures: []int32{fs.signature},
  172. }
  173. glog.V(1).Infof("mkdir: %v", request)
  174. if err := filer_pb.CreateEntry(client, request); err != nil {
  175. return fmt.Errorf("mkdir %s/%s: %v", dir, name, err)
  176. }
  177. return nil
  178. })
  179. }
  180. func (fs *WebDavFileSystem) OpenFile(ctx context.Context, fullFilePath string, flag int, perm os.FileMode) (webdav.File, error) {
  181. glog.V(2).Infof("WebDavFileSystem.OpenFile %v %x", fullFilePath, flag)
  182. var err error
  183. if fullFilePath, err = clearName(fullFilePath); err != nil {
  184. return nil, err
  185. }
  186. if flag&os.O_CREATE != 0 {
  187. // file should not have / suffix.
  188. if strings.HasSuffix(fullFilePath, "/") {
  189. return nil, os.ErrInvalid
  190. }
  191. _, err = fs.stat(ctx, fullFilePath)
  192. if err == nil {
  193. if flag&os.O_EXCL != 0 {
  194. return nil, os.ErrExist
  195. }
  196. fs.removeAll(ctx, fullFilePath)
  197. }
  198. dir, name := util.FullPath(fullFilePath).DirAndName()
  199. err = fs.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error {
  200. if err := filer_pb.CreateEntry(client, &filer_pb.CreateEntryRequest{
  201. Directory: dir,
  202. Entry: &filer_pb.Entry{
  203. Name: name,
  204. IsDirectory: perm&os.ModeDir > 0,
  205. Attributes: &filer_pb.FuseAttributes{
  206. Mtime: time.Now().Unix(),
  207. Crtime: time.Now().Unix(),
  208. FileMode: uint32(perm),
  209. Uid: fs.option.Uid,
  210. Gid: fs.option.Gid,
  211. TtlSec: 0,
  212. },
  213. },
  214. Signatures: []int32{fs.signature},
  215. }); err != nil {
  216. return fmt.Errorf("create %s: %v", fullFilePath, err)
  217. }
  218. return nil
  219. })
  220. if err != nil {
  221. return nil, err
  222. }
  223. return &WebDavFile{
  224. fs: fs,
  225. name: fullFilePath,
  226. isDirectory: false,
  227. bufWriter: buffered_writer.NewBufferedWriteCloser(4 * 1024 * 1024),
  228. }, nil
  229. }
  230. fi, err := fs.stat(ctx, fullFilePath)
  231. if err != nil {
  232. return nil, os.ErrNotExist
  233. }
  234. if !strings.HasSuffix(fullFilePath, "/") && fi.IsDir() {
  235. fullFilePath += "/"
  236. }
  237. return &WebDavFile{
  238. fs: fs,
  239. name: fullFilePath,
  240. isDirectory: false,
  241. bufWriter: buffered_writer.NewBufferedWriteCloser(4 * 1024 * 1024),
  242. }, nil
  243. }
  244. func (fs *WebDavFileSystem) removeAll(ctx context.Context, fullFilePath string) error {
  245. var err error
  246. if fullFilePath, err = clearName(fullFilePath); err != nil {
  247. return err
  248. }
  249. dir, name := util.FullPath(fullFilePath).DirAndName()
  250. return filer_pb.Remove(fs, dir, name, true, false, false, false, []int32{fs.signature})
  251. }
  252. func (fs *WebDavFileSystem) RemoveAll(ctx context.Context, name string) error {
  253. glog.V(2).Infof("WebDavFileSystem.RemoveAll %v", name)
  254. return fs.removeAll(ctx, name)
  255. }
  256. func (fs *WebDavFileSystem) Rename(ctx context.Context, oldName, newName string) error {
  257. glog.V(2).Infof("WebDavFileSystem.Rename %v to %v", oldName, newName)
  258. var err error
  259. if oldName, err = clearName(oldName); err != nil {
  260. return err
  261. }
  262. if newName, err = clearName(newName); err != nil {
  263. return err
  264. }
  265. of, err := fs.stat(ctx, oldName)
  266. if err != nil {
  267. return os.ErrExist
  268. }
  269. if of.IsDir() {
  270. if strings.HasSuffix(oldName, "/") {
  271. oldName = strings.TrimRight(oldName, "/")
  272. }
  273. if strings.HasSuffix(newName, "/") {
  274. newName = strings.TrimRight(newName, "/")
  275. }
  276. }
  277. _, err = fs.stat(ctx, newName)
  278. if err == nil {
  279. return os.ErrExist
  280. }
  281. oldDir, oldBaseName := util.FullPath(oldName).DirAndName()
  282. newDir, newBaseName := util.FullPath(newName).DirAndName()
  283. return fs.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error {
  284. request := &filer_pb.AtomicRenameEntryRequest{
  285. OldDirectory: oldDir,
  286. OldName: oldBaseName,
  287. NewDirectory: newDir,
  288. NewName: newBaseName,
  289. }
  290. _, err := client.AtomicRenameEntry(ctx, request)
  291. if err != nil {
  292. return fmt.Errorf("renaming %s/%s => %s/%s: %v", oldDir, oldBaseName, newDir, newBaseName, err)
  293. }
  294. return nil
  295. })
  296. }
  297. func (fs *WebDavFileSystem) stat(ctx context.Context, fullFilePath string) (os.FileInfo, error) {
  298. var err error
  299. if fullFilePath, err = clearName(fullFilePath); err != nil {
  300. return nil, err
  301. }
  302. fullpath := util.FullPath(fullFilePath)
  303. var fi FileInfo
  304. entry, err := filer_pb.GetEntry(fs, fullpath)
  305. if entry == nil {
  306. return nil, os.ErrNotExist
  307. }
  308. if err != nil {
  309. return nil, err
  310. }
  311. fi.size = int64(filer.FileSize(entry))
  312. fi.name = string(fullpath)
  313. fi.mode = os.FileMode(entry.Attributes.FileMode)
  314. fi.modifiedTime = time.Unix(entry.Attributes.Mtime, 0)
  315. fi.etag = filer.ETag(entry)
  316. fi.isDirectory = entry.IsDirectory
  317. if fi.name == "/" {
  318. fi.modifiedTime = time.Now()
  319. fi.isDirectory = true
  320. }
  321. return &fi, nil
  322. }
  323. func (fs *WebDavFileSystem) Stat(ctx context.Context, name string) (os.FileInfo, error) {
  324. glog.V(2).Infof("WebDavFileSystem.Stat %v", name)
  325. return fs.stat(ctx, name)
  326. }
  327. func (f *WebDavFile) saveDataAsChunk(reader io.Reader, name string, offset int64, tsNs int64) (chunk *filer_pb.FileChunk, err error) {
  328. fileId, uploadResult, flushErr, _ := operation.UploadWithRetry(
  329. f.fs,
  330. &filer_pb.AssignVolumeRequest{
  331. Count: 1,
  332. Replication: f.fs.option.Replication,
  333. Collection: f.fs.option.Collection,
  334. DiskType: f.fs.option.DiskType,
  335. Path: name,
  336. },
  337. &operation.UploadOption{
  338. Filename: f.name,
  339. Cipher: f.fs.option.Cipher,
  340. IsInputCompressed: false,
  341. MimeType: "",
  342. PairMap: nil,
  343. },
  344. func(host, fileId string) string {
  345. return fmt.Sprintf("http://%s/%s", host, fileId)
  346. },
  347. reader,
  348. )
  349. if flushErr != nil {
  350. glog.V(0).Infof("upload data %v: %v", f.name, flushErr)
  351. return nil, fmt.Errorf("upload data: %v", flushErr)
  352. }
  353. if uploadResult.Error != "" {
  354. glog.V(0).Infof("upload failure %v: %v", f.name, flushErr)
  355. return nil, fmt.Errorf("upload result: %v", uploadResult.Error)
  356. }
  357. return uploadResult.ToPbFileChunk(fileId, offset, tsNs), nil
  358. }
  359. func (f *WebDavFile) Write(buf []byte) (int, error) {
  360. glog.V(2).Infof("WebDavFileSystem.Write %v", f.name)
  361. dir, _ := util.FullPath(f.name).DirAndName()
  362. var getErr error
  363. ctx := context.Background()
  364. if f.entry == nil {
  365. f.entry, getErr = filer_pb.GetEntry(f.fs, util.FullPath(f.name))
  366. }
  367. if f.entry == nil {
  368. return 0, getErr
  369. }
  370. if getErr != nil {
  371. return 0, getErr
  372. }
  373. if f.bufWriter.FlushFunc == nil {
  374. f.bufWriter.FlushFunc = func(data []byte, offset int64) (flushErr error) {
  375. var chunk *filer_pb.FileChunk
  376. chunk, flushErr = f.saveDataAsChunk(util.NewBytesReader(data), f.name, offset, time.Now().UnixNano())
  377. if flushErr != nil {
  378. return fmt.Errorf("%s upload result: %v", f.name, flushErr)
  379. }
  380. f.entry.Content = nil
  381. f.entry.Chunks = append(f.entry.GetChunks(), chunk)
  382. return flushErr
  383. }
  384. f.bufWriter.CloseFunc = func() error {
  385. manifestedChunks, manifestErr := filer.MaybeManifestize(f.saveDataAsChunk, f.entry.GetChunks())
  386. if manifestErr != nil {
  387. // not good, but should be ok
  388. glog.V(0).Infof("file %s close MaybeManifestize: %v", f.name, manifestErr)
  389. } else {
  390. f.entry.Chunks = manifestedChunks
  391. }
  392. flushErr := f.fs.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error {
  393. f.entry.Attributes.Mtime = time.Now().Unix()
  394. request := &filer_pb.UpdateEntryRequest{
  395. Directory: dir,
  396. Entry: f.entry,
  397. Signatures: []int32{f.fs.signature},
  398. }
  399. if _, err := client.UpdateEntry(ctx, request); err != nil {
  400. return fmt.Errorf("update %s: %v", f.name, err)
  401. }
  402. return nil
  403. })
  404. return flushErr
  405. }
  406. }
  407. written, err := f.bufWriter.Write(buf)
  408. if err == nil {
  409. f.entry.Attributes.FileSize = uint64(max(f.off+int64(written), int64(f.entry.Attributes.FileSize)))
  410. glog.V(3).Infof("WebDavFileSystem.Write %v: written [%d,%d)", f.name, f.off, f.off+int64(len(buf)))
  411. f.off += int64(written)
  412. }
  413. return written, err
  414. }
  415. func (f *WebDavFile) Close() error {
  416. glog.V(2).Infof("WebDavFileSystem.Close %v", f.name)
  417. err := f.bufWriter.Close()
  418. if f.entry != nil {
  419. f.entry = nil
  420. f.visibleIntervals = nil
  421. }
  422. return err
  423. }
  424. func (f *WebDavFile) Read(p []byte) (readSize int, err error) {
  425. glog.V(2).Infof("WebDavFileSystem.Read %v", f.name)
  426. if f.entry == nil {
  427. f.entry, err = filer_pb.GetEntry(f.fs, util.FullPath(f.name))
  428. }
  429. if f.entry == nil {
  430. return 0, err
  431. }
  432. if err != nil {
  433. return 0, err
  434. }
  435. fileSize := int64(filer.FileSize(f.entry))
  436. if fileSize == 0 {
  437. return 0, io.EOF
  438. }
  439. if f.visibleIntervals == nil {
  440. f.visibleIntervals, _ = filer.NonOverlappingVisibleIntervals(filer.LookupFn(f.fs), f.entry.GetChunks(), 0, fileSize)
  441. f.reader = nil
  442. }
  443. if f.reader == nil {
  444. chunkViews := filer.ViewFromVisibleIntervals(f.visibleIntervals, 0, fileSize)
  445. f.reader = filer.NewChunkReaderAtFromClient(f.fs.readerCache, chunkViews, fileSize)
  446. }
  447. readSize, err = f.reader.ReadAt(p, f.off)
  448. glog.V(3).Infof("WebDavFileSystem.Read %v: [%d,%d)", f.name, f.off, f.off+int64(readSize))
  449. f.off += int64(readSize)
  450. if err != nil && err != io.EOF {
  451. glog.Errorf("file read %s: %v", f.name, err)
  452. }
  453. return
  454. }
  455. func (f *WebDavFile) Readdir(count int) (ret []os.FileInfo, err error) {
  456. glog.V(2).Infof("WebDavFileSystem.Readdir %v count %d", f.name, count)
  457. dir, _ := util.FullPath(f.name).DirAndName()
  458. err = filer_pb.ReadDirAllEntries(f.fs, util.FullPath(dir), "", func(entry *filer_pb.Entry, isLast bool) error {
  459. fi := FileInfo{
  460. size: int64(filer.FileSize(entry)),
  461. name: entry.Name,
  462. mode: os.FileMode(entry.Attributes.FileMode),
  463. modifiedTime: time.Unix(entry.Attributes.Mtime, 0),
  464. isDirectory: entry.IsDirectory,
  465. }
  466. if !strings.HasSuffix(fi.name, "/") && fi.IsDir() {
  467. fi.name += "/"
  468. }
  469. glog.V(4).Infof("entry: %v", fi.name)
  470. ret = append(ret, &fi)
  471. return nil
  472. })
  473. if err != nil {
  474. return nil, err
  475. }
  476. old := f.off
  477. if old >= int64(len(ret)) {
  478. if count > 0 {
  479. return nil, io.EOF
  480. }
  481. return nil, nil
  482. }
  483. if count > 0 {
  484. f.off += int64(count)
  485. if f.off > int64(len(ret)) {
  486. f.off = int64(len(ret))
  487. }
  488. } else {
  489. f.off = int64(len(ret))
  490. old = 0
  491. }
  492. return ret[old:f.off], nil
  493. }
  494. func (f *WebDavFile) Seek(offset int64, whence int) (int64, error) {
  495. glog.V(2).Infof("WebDavFile.Seek %v %v %v", f.name, offset, whence)
  496. ctx := context.Background()
  497. var err error
  498. switch whence {
  499. case io.SeekStart:
  500. f.off = 0
  501. case io.SeekEnd:
  502. if fi, err := f.fs.stat(ctx, f.name); err != nil {
  503. return 0, err
  504. } else {
  505. f.off = fi.Size()
  506. }
  507. }
  508. f.off += offset
  509. return f.off, err
  510. }
  511. func (f *WebDavFile) Stat() (os.FileInfo, error) {
  512. glog.V(2).Infof("WebDavFile.Stat %v", f.name)
  513. ctx := context.Background()
  514. return f.fs.stat(ctx, f.name)
  515. }