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.

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