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.

617 lines
15 KiB

5 years ago
5 years ago
3 years ago
3 years ago
4 years ago
2 years ago
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
2 years ago
4 years ago
5 years ago
5 years ago
5 years ago
4 years ago
5 years ago
5 years ago
5 years ago
2 years ago
2 years ago
4 years ago
4 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. ws = &WebDavServer{
  57. option: option,
  58. grpcDialOption: security.LoadClientTLS(util.GetViper(), "grpc.filer"),
  59. Handler: &webdav.Handler{
  60. FileSystem: fs,
  61. LockSystem: webdav.NewMemLS(),
  62. },
  63. }
  64. return ws, nil
  65. }
  66. // adapted from https://github.com/mattn/davfs/blob/master/plugin/mysql/mysql.go
  67. type WebDavFileSystem struct {
  68. option *WebDavOption
  69. secret security.SigningKey
  70. grpcDialOption grpc.DialOption
  71. chunkCache *chunk_cache.TieredChunkCache
  72. signature int32
  73. }
  74. type FileInfo struct {
  75. name string
  76. size int64
  77. mode os.FileMode
  78. modifiedTime time.Time
  79. isDirectory bool
  80. }
  81. func (fi *FileInfo) Name() string { return fi.name }
  82. func (fi *FileInfo) Size() int64 { return fi.size }
  83. func (fi *FileInfo) Mode() os.FileMode { return fi.mode }
  84. func (fi *FileInfo) ModTime() time.Time { return fi.modifiedTime }
  85. func (fi *FileInfo) IsDir() bool { return fi.isDirectory }
  86. func (fi *FileInfo) Sys() interface{} { return nil }
  87. type WebDavFile struct {
  88. fs *WebDavFileSystem
  89. name string
  90. isDirectory bool
  91. off int64
  92. entry *filer_pb.Entry
  93. entryViewCache []filer.VisibleInterval
  94. reader io.ReaderAt
  95. bufWriter *buffered_writer.BufferedWriteCloser
  96. }
  97. func NewWebDavFileSystem(option *WebDavOption) (webdav.FileSystem, error) {
  98. cacheUniqueId := util.Md5String([]byte("webdav" + string(option.Filer) + util.Version()))[0:8]
  99. cacheDir := path.Join(option.CacheDir, cacheUniqueId)
  100. os.MkdirAll(cacheDir, os.FileMode(0755))
  101. chunkCache := chunk_cache.NewTieredChunkCache(256, cacheDir, option.CacheSizeMB, 1024*1024)
  102. return &WebDavFileSystem{
  103. option: option,
  104. chunkCache: chunkCache,
  105. signature: util.RandomInt32(),
  106. }, nil
  107. }
  108. var _ = filer_pb.FilerClient(&WebDavFileSystem{})
  109. func (fs *WebDavFileSystem) WithFilerClient(streamingMode bool, fn func(filer_pb.SeaweedFilerClient) error) error {
  110. return pb.WithGrpcClient(streamingMode, func(grpcConnection *grpc.ClientConn) error {
  111. client := filer_pb.NewSeaweedFilerClient(grpcConnection)
  112. return fn(client)
  113. }, fs.option.Filer.ToGrpcAddress(), false, fs.option.GrpcDialOption)
  114. }
  115. func (fs *WebDavFileSystem) AdjustedUrl(location *filer_pb.Location) string {
  116. return location.Url
  117. }
  118. func (fs *WebDavFileSystem) GetDataCenter() string {
  119. return ""
  120. }
  121. func clearName(name string) (string, error) {
  122. slashed := strings.HasSuffix(name, "/")
  123. name = path.Clean(name)
  124. if !strings.HasSuffix(name, "/") && slashed {
  125. name += "/"
  126. }
  127. if !strings.HasPrefix(name, "/") {
  128. return "", os.ErrInvalid
  129. }
  130. return name, nil
  131. }
  132. func (fs *WebDavFileSystem) Mkdir(ctx context.Context, fullDirPath string, perm os.FileMode) error {
  133. glog.V(2).Infof("WebDavFileSystem.Mkdir %v", fullDirPath)
  134. if !strings.HasSuffix(fullDirPath, "/") {
  135. fullDirPath += "/"
  136. }
  137. var err error
  138. if fullDirPath, err = clearName(fullDirPath); err != nil {
  139. return err
  140. }
  141. _, err = fs.stat(ctx, fullDirPath)
  142. if err == nil {
  143. return os.ErrExist
  144. }
  145. return fs.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error {
  146. dir, name := util.FullPath(fullDirPath).DirAndName()
  147. request := &filer_pb.CreateEntryRequest{
  148. Directory: dir,
  149. Entry: &filer_pb.Entry{
  150. Name: name,
  151. IsDirectory: true,
  152. Attributes: &filer_pb.FuseAttributes{
  153. Mtime: time.Now().Unix(),
  154. Crtime: time.Now().Unix(),
  155. FileMode: uint32(perm | os.ModeDir),
  156. Uid: fs.option.Uid,
  157. Gid: fs.option.Gid,
  158. },
  159. },
  160. Signatures: []int32{fs.signature},
  161. }
  162. glog.V(1).Infof("mkdir: %v", request)
  163. if err := filer_pb.CreateEntry(client, request); err != nil {
  164. return fmt.Errorf("mkdir %s/%s: %v", dir, name, err)
  165. }
  166. return nil
  167. })
  168. }
  169. func (fs *WebDavFileSystem) OpenFile(ctx context.Context, fullFilePath string, flag int, perm os.FileMode) (webdav.File, error) {
  170. // Add filer.path
  171. fullFilePath = fs.option.FilerRootPath + fullFilePath
  172. glog.V(2).Infof("WebDavFileSystem.OpenFile %v %x", fullFilePath, flag)
  173. var err error
  174. if fullFilePath, err = clearName(fullFilePath); err != nil {
  175. return nil, err
  176. }
  177. if flag&os.O_CREATE != 0 {
  178. // file should not have / suffix.
  179. if strings.HasSuffix(fullFilePath, "/") {
  180. return nil, os.ErrInvalid
  181. }
  182. _, err = fs.stat(ctx, fullFilePath)
  183. if err == nil {
  184. if flag&os.O_EXCL != 0 {
  185. return nil, os.ErrExist
  186. }
  187. fs.removeAll(ctx, fullFilePath)
  188. }
  189. dir, name := util.FullPath(fullFilePath).DirAndName()
  190. err = fs.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error {
  191. if err := filer_pb.CreateEntry(client, &filer_pb.CreateEntryRequest{
  192. Directory: dir,
  193. Entry: &filer_pb.Entry{
  194. Name: name,
  195. IsDirectory: perm&os.ModeDir > 0,
  196. Attributes: &filer_pb.FuseAttributes{
  197. Mtime: time.Now().Unix(),
  198. Crtime: time.Now().Unix(),
  199. FileMode: uint32(perm),
  200. Uid: fs.option.Uid,
  201. Gid: fs.option.Gid,
  202. TtlSec: 0,
  203. },
  204. },
  205. Signatures: []int32{fs.signature},
  206. }); err != nil {
  207. return fmt.Errorf("create %s: %v", fullFilePath, err)
  208. }
  209. return nil
  210. })
  211. if err != nil {
  212. return nil, err
  213. }
  214. return &WebDavFile{
  215. fs: fs,
  216. name: fullFilePath,
  217. isDirectory: false,
  218. bufWriter: buffered_writer.NewBufferedWriteCloser(4 * 1024 * 1024),
  219. }, nil
  220. }
  221. fi, err := fs.stat(ctx, fullFilePath)
  222. if err != nil {
  223. return nil, os.ErrNotExist
  224. }
  225. if !strings.HasSuffix(fullFilePath, "/") && fi.IsDir() {
  226. fullFilePath += "/"
  227. }
  228. return &WebDavFile{
  229. fs: fs,
  230. name: fullFilePath,
  231. isDirectory: false,
  232. bufWriter: buffered_writer.NewBufferedWriteCloser(4 * 1024 * 1024),
  233. }, nil
  234. }
  235. func (fs *WebDavFileSystem) removeAll(ctx context.Context, fullFilePath string) error {
  236. var err error
  237. if fullFilePath, err = clearName(fullFilePath); err != nil {
  238. return err
  239. }
  240. dir, name := util.FullPath(fullFilePath).DirAndName()
  241. return filer_pb.Remove(fs, dir, name, true, false, false, false, []int32{fs.signature})
  242. }
  243. func (fs *WebDavFileSystem) RemoveAll(ctx context.Context, name string) error {
  244. glog.V(2).Infof("WebDavFileSystem.RemoveAll %v", name)
  245. return fs.removeAll(ctx, name)
  246. }
  247. func (fs *WebDavFileSystem) Rename(ctx context.Context, oldName, newName string) error {
  248. glog.V(2).Infof("WebDavFileSystem.Rename %v to %v", oldName, newName)
  249. var err error
  250. if oldName, err = clearName(oldName); err != nil {
  251. return err
  252. }
  253. if newName, err = clearName(newName); err != nil {
  254. return err
  255. }
  256. of, err := fs.stat(ctx, oldName)
  257. if err != nil {
  258. return os.ErrExist
  259. }
  260. if of.IsDir() {
  261. if strings.HasSuffix(oldName, "/") {
  262. oldName = strings.TrimRight(oldName, "/")
  263. }
  264. if strings.HasSuffix(newName, "/") {
  265. newName = strings.TrimRight(newName, "/")
  266. }
  267. }
  268. _, err = fs.stat(ctx, newName)
  269. if err == nil {
  270. return os.ErrExist
  271. }
  272. oldDir, oldBaseName := util.FullPath(oldName).DirAndName()
  273. newDir, newBaseName := util.FullPath(newName).DirAndName()
  274. return fs.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error {
  275. request := &filer_pb.AtomicRenameEntryRequest{
  276. OldDirectory: oldDir,
  277. OldName: oldBaseName,
  278. NewDirectory: newDir,
  279. NewName: newBaseName,
  280. }
  281. _, err := client.AtomicRenameEntry(ctx, request)
  282. if err != nil {
  283. return fmt.Errorf("renaming %s/%s => %s/%s: %v", oldDir, oldBaseName, newDir, newBaseName, err)
  284. }
  285. return nil
  286. })
  287. }
  288. func (fs *WebDavFileSystem) stat(ctx context.Context, fullFilePath string) (os.FileInfo, error) {
  289. var err error
  290. if fullFilePath, err = clearName(fullFilePath); err != nil {
  291. return nil, err
  292. }
  293. fullpath := util.FullPath(fullFilePath)
  294. var fi FileInfo
  295. entry, err := filer_pb.GetEntry(fs, fullpath)
  296. if entry == nil {
  297. return nil, os.ErrNotExist
  298. }
  299. if err != nil {
  300. return nil, err
  301. }
  302. fi.size = int64(filer.FileSize(entry))
  303. fi.name = string(fullpath)
  304. fi.mode = os.FileMode(entry.Attributes.FileMode)
  305. fi.modifiedTime = time.Unix(entry.Attributes.Mtime, 0)
  306. fi.isDirectory = entry.IsDirectory
  307. if fi.name == "/" {
  308. fi.modifiedTime = time.Now()
  309. fi.isDirectory = true
  310. }
  311. return &fi, nil
  312. }
  313. func (fs *WebDavFileSystem) Stat(ctx context.Context, name string) (os.FileInfo, error) {
  314. // Add filer.path
  315. name = fs.option.FilerRootPath + name
  316. glog.V(2).Infof("WebDavFileSystem.Stat %v", name)
  317. return fs.stat(ctx, name)
  318. }
  319. func (f *WebDavFile) saveDataAsChunk(reader io.Reader, name string, offset int64) (chunk *filer_pb.FileChunk, err error) {
  320. fileId, uploadResult, flushErr, _ := operation.UploadWithRetry(
  321. f.fs,
  322. &filer_pb.AssignVolumeRequest{
  323. Count: 1,
  324. Replication: f.fs.option.Replication,
  325. Collection: f.fs.option.Collection,
  326. DiskType: f.fs.option.DiskType,
  327. Path: name,
  328. },
  329. &operation.UploadOption{
  330. Filename: f.name,
  331. Cipher: f.fs.option.Cipher,
  332. IsInputCompressed: false,
  333. MimeType: "",
  334. PairMap: nil,
  335. },
  336. func(host, fileId string) string {
  337. return fmt.Sprintf("http://%s/%s", host, fileId)
  338. },
  339. reader,
  340. )
  341. if flushErr != nil {
  342. glog.V(0).Infof("upload data %v: %v", f.name, flushErr)
  343. return nil, fmt.Errorf("upload data: %v", flushErr)
  344. }
  345. if uploadResult.Error != "" {
  346. glog.V(0).Infof("upload failure %v: %v", f.name, flushErr)
  347. return nil, fmt.Errorf("upload result: %v", uploadResult.Error)
  348. }
  349. return uploadResult.ToPbFileChunk(fileId, offset), nil
  350. }
  351. func (f *WebDavFile) Write(buf []byte) (int, error) {
  352. glog.V(2).Infof("WebDavFileSystem.Write %v", f.name)
  353. dir, _ := util.FullPath(f.name).DirAndName()
  354. var getErr error
  355. ctx := context.Background()
  356. if f.entry == nil {
  357. f.entry, getErr = filer_pb.GetEntry(f.fs, util.FullPath(f.name))
  358. }
  359. if f.entry == nil {
  360. return 0, getErr
  361. }
  362. if getErr != nil {
  363. return 0, getErr
  364. }
  365. if f.bufWriter.FlushFunc == nil {
  366. f.bufWriter.FlushFunc = func(data []byte, offset int64) (flushErr error) {
  367. var chunk *filer_pb.FileChunk
  368. chunk, flushErr = f.saveDataAsChunk(util.NewBytesReader(data), f.name, offset)
  369. if flushErr != nil {
  370. return fmt.Errorf("%s upload result: %v", f.name, flushErr)
  371. }
  372. f.entry.Content = nil
  373. f.entry.Chunks = append(f.entry.GetChunks(), chunk)
  374. return flushErr
  375. }
  376. f.bufWriter.CloseFunc = func() error {
  377. manifestedChunks, manifestErr := filer.MaybeManifestize(f.saveDataAsChunk, f.entry.GetChunks())
  378. if manifestErr != nil {
  379. // not good, but should be ok
  380. glog.V(0).Infof("file %s close MaybeManifestize: %v", f.name, manifestErr)
  381. } else {
  382. f.entry.Chunks = manifestedChunks
  383. }
  384. flushErr := f.fs.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error {
  385. f.entry.Attributes.Mtime = time.Now().Unix()
  386. request := &filer_pb.UpdateEntryRequest{
  387. Directory: dir,
  388. Entry: f.entry,
  389. Signatures: []int32{f.fs.signature},
  390. }
  391. if _, err := client.UpdateEntry(ctx, request); err != nil {
  392. return fmt.Errorf("update %s: %v", f.name, err)
  393. }
  394. return nil
  395. })
  396. return flushErr
  397. }
  398. }
  399. written, err := f.bufWriter.Write(buf)
  400. if err == nil {
  401. f.entry.Attributes.FileSize = uint64(max(f.off+int64(written), int64(f.entry.Attributes.FileSize)))
  402. glog.V(3).Infof("WebDavFileSystem.Write %v: written [%d,%d)", f.name, f.off, f.off+int64(len(buf)))
  403. f.off += int64(written)
  404. }
  405. return written, err
  406. }
  407. func (f *WebDavFile) Close() error {
  408. glog.V(2).Infof("WebDavFileSystem.Close %v", f.name)
  409. err := f.bufWriter.Close()
  410. if f.entry != nil {
  411. f.entry = nil
  412. f.entryViewCache = nil
  413. }
  414. return err
  415. }
  416. func (f *WebDavFile) Read(p []byte) (readSize int, err error) {
  417. glog.V(2).Infof("WebDavFileSystem.Read %v", f.name)
  418. if f.entry == nil {
  419. f.entry, err = filer_pb.GetEntry(f.fs, util.FullPath(f.name))
  420. }
  421. if f.entry == nil {
  422. return 0, err
  423. }
  424. if err != nil {
  425. return 0, err
  426. }
  427. fileSize := int64(filer.FileSize(f.entry))
  428. if fileSize == 0 {
  429. return 0, io.EOF
  430. }
  431. if f.entryViewCache == nil {
  432. f.entryViewCache, _ = filer.NonOverlappingVisibleIntervals(filer.LookupFn(f.fs), f.entry.GetChunks(), 0, fileSize)
  433. f.reader = nil
  434. }
  435. if f.reader == nil {
  436. chunkViews := filer.ViewFromVisibleIntervals(f.entryViewCache, 0, fileSize)
  437. f.reader = filer.NewChunkReaderAtFromClient(filer.LookupFn(f.fs), chunkViews, f.fs.chunkCache, fileSize)
  438. }
  439. readSize, err = f.reader.ReadAt(p, f.off)
  440. glog.V(3).Infof("WebDavFileSystem.Read %v: [%d,%d)", f.name, f.off, f.off+int64(readSize))
  441. f.off += int64(readSize)
  442. if err != nil && err != io.EOF {
  443. glog.Errorf("file read %s: %v", f.name, err)
  444. }
  445. return
  446. }
  447. func (f *WebDavFile) Readdir(count int) (ret []os.FileInfo, err error) {
  448. glog.V(2).Infof("WebDavFileSystem.Readdir %v count %d", f.name, count)
  449. dir, _ := util.FullPath(f.name).DirAndName()
  450. err = filer_pb.ReadDirAllEntries(f.fs, util.FullPath(dir), "", func(entry *filer_pb.Entry, isLast bool) error {
  451. fi := FileInfo{
  452. size: int64(filer.FileSize(entry)),
  453. name: entry.Name,
  454. mode: os.FileMode(entry.Attributes.FileMode),
  455. modifiedTime: time.Unix(entry.Attributes.Mtime, 0),
  456. isDirectory: entry.IsDirectory,
  457. }
  458. if !strings.HasSuffix(fi.name, "/") && fi.IsDir() {
  459. fi.name += "/"
  460. }
  461. glog.V(4).Infof("entry: %v", fi.name)
  462. ret = append(ret, &fi)
  463. return nil
  464. })
  465. old := f.off
  466. if old >= int64(len(ret)) {
  467. if count > 0 {
  468. return nil, io.EOF
  469. }
  470. return nil, nil
  471. }
  472. if count > 0 {
  473. f.off += int64(count)
  474. if f.off > int64(len(ret)) {
  475. f.off = int64(len(ret))
  476. }
  477. } else {
  478. f.off = int64(len(ret))
  479. old = 0
  480. }
  481. return ret[old:f.off], nil
  482. }
  483. func (f *WebDavFile) Seek(offset int64, whence int) (int64, error) {
  484. glog.V(2).Infof("WebDavFile.Seek %v %v %v", f.name, offset, whence)
  485. ctx := context.Background()
  486. var err error
  487. switch whence {
  488. case io.SeekStart:
  489. f.off = 0
  490. case io.SeekEnd:
  491. if fi, err := f.fs.stat(ctx, f.name); err != nil {
  492. return 0, err
  493. } else {
  494. f.off = fi.Size()
  495. }
  496. }
  497. f.off += offset
  498. return f.off, err
  499. }
  500. func (f *WebDavFile) Stat() (os.FileInfo, error) {
  501. glog.V(2).Infof("WebDavFile.Stat %v", f.name)
  502. ctx := context.Background()
  503. return f.fs.stat(ctx, f.name)
  504. }