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.

610 lines
15 KiB

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