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.

576 lines
14 KiB

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