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.

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