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.

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