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