request-example.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647
  1. package sftp
  2. // This serves as an example of how to implement the request server handler as
  3. // well as a dummy backend for testing. It implements an in-memory backend that
  4. // works as a very simple filesystem with simple flat key-value lookup system.
  5. import (
  6. "errors"
  7. "io"
  8. "os"
  9. "path"
  10. "sort"
  11. "strings"
  12. "sync"
  13. "syscall"
  14. "time"
  15. )
  16. const maxSymlinkFollows = 5
  17. var errTooManySymlinks = errors.New("too many symbolic links")
  18. // InMemHandler returns a Hanlders object with the test handlers.
  19. func InMemHandler() Handlers {
  20. root := &root{
  21. rootFile: &memFile{name: "/", modtime: time.Now(), isdir: true},
  22. files: make(map[string]*memFile),
  23. }
  24. return Handlers{root, root, root, root}
  25. }
  26. // Example Handlers
  27. func (fs *root) Fileread(r *Request) (io.ReaderAt, error) {
  28. flags := r.Pflags()
  29. if !flags.Read {
  30. // sanity check
  31. return nil, os.ErrInvalid
  32. }
  33. return fs.OpenFile(r)
  34. }
  35. func (fs *root) Filewrite(r *Request) (io.WriterAt, error) {
  36. flags := r.Pflags()
  37. if !flags.Write {
  38. // sanity check
  39. return nil, os.ErrInvalid
  40. }
  41. return fs.OpenFile(r)
  42. }
  43. func (fs *root) OpenFile(r *Request) (WriterAtReaderAt, error) {
  44. if fs.mockErr != nil {
  45. return nil, fs.mockErr
  46. }
  47. _ = r.WithContext(r.Context()) // initialize context for deadlock testing
  48. fs.mu.Lock()
  49. defer fs.mu.Unlock()
  50. return fs.openfile(r.Filepath, r.Flags)
  51. }
  52. func (fs *root) putfile(pathname string, file *memFile) error {
  53. pathname, err := fs.canonName(pathname)
  54. if err != nil {
  55. return err
  56. }
  57. if !strings.HasPrefix(pathname, "/") {
  58. return os.ErrInvalid
  59. }
  60. if _, err := fs.lfetch(pathname); err != os.ErrNotExist {
  61. return os.ErrExist
  62. }
  63. file.name = pathname
  64. fs.files[pathname] = file
  65. return nil
  66. }
  67. func (fs *root) openfile(pathname string, flags uint32) (*memFile, error) {
  68. pflags := newFileOpenFlags(flags)
  69. file, err := fs.fetch(pathname)
  70. if err == os.ErrNotExist {
  71. if !pflags.Creat {
  72. return nil, os.ErrNotExist
  73. }
  74. var count int
  75. // You can create files through dangling symlinks.
  76. link, err := fs.lfetch(pathname)
  77. for err == nil && link.symlink != "" {
  78. if pflags.Excl {
  79. // unless you also passed in O_EXCL
  80. return nil, os.ErrInvalid
  81. }
  82. if count++; count > maxSymlinkFollows {
  83. return nil, errTooManySymlinks
  84. }
  85. pathname = link.symlink
  86. link, err = fs.lfetch(pathname)
  87. }
  88. file := &memFile{
  89. modtime: time.Now(),
  90. }
  91. if err := fs.putfile(pathname, file); err != nil {
  92. return nil, err
  93. }
  94. return file, nil
  95. }
  96. if err != nil {
  97. return nil, err
  98. }
  99. if pflags.Creat && pflags.Excl {
  100. return nil, os.ErrExist
  101. }
  102. if file.IsDir() {
  103. return nil, os.ErrInvalid
  104. }
  105. if pflags.Trunc {
  106. if err := file.Truncate(0); err != nil {
  107. return nil, err
  108. }
  109. }
  110. return file, nil
  111. }
  112. func (fs *root) Filecmd(r *Request) error {
  113. if fs.mockErr != nil {
  114. return fs.mockErr
  115. }
  116. _ = r.WithContext(r.Context()) // initialize context for deadlock testing
  117. fs.mu.Lock()
  118. defer fs.mu.Unlock()
  119. switch r.Method {
  120. case "Setstat":
  121. file, err := fs.openfile(r.Filepath, sshFxfWrite)
  122. if err != nil {
  123. return err
  124. }
  125. if r.AttrFlags().Size {
  126. return file.Truncate(int64(r.Attributes().Size))
  127. }
  128. return nil
  129. case "Rename":
  130. // SFTP-v2: "It is an error if there already exists a file with the name specified by newpath."
  131. // This varies from the POSIX specification, which allows limited replacement of target files.
  132. if fs.exists(r.Target) {
  133. return os.ErrExist
  134. }
  135. return fs.rename(r.Filepath, r.Target)
  136. case "Rmdir":
  137. return fs.rmdir(r.Filepath)
  138. case "Remove":
  139. // IEEE 1003.1 remove explicitly can unlink files and remove empty directories.
  140. // We use instead here the semantics of unlink, which is allowed to be restricted against directories.
  141. return fs.unlink(r.Filepath)
  142. case "Mkdir":
  143. return fs.mkdir(r.Filepath)
  144. case "Link":
  145. return fs.link(r.Filepath, r.Target)
  146. case "Symlink":
  147. // NOTE: r.Filepath is the target, and r.Target is the linkpath.
  148. return fs.symlink(r.Filepath, r.Target)
  149. }
  150. return errors.New("unsupported")
  151. }
  152. func (fs *root) rename(oldpath, newpath string) error {
  153. file, err := fs.lfetch(oldpath)
  154. if err != nil {
  155. return err
  156. }
  157. newpath, err = fs.canonName(newpath)
  158. if err != nil {
  159. return err
  160. }
  161. if !strings.HasPrefix(newpath, "/") {
  162. return os.ErrInvalid
  163. }
  164. target, err := fs.lfetch(newpath)
  165. if err != os.ErrNotExist {
  166. if target == file {
  167. // IEEE 1003.1: if oldpath and newpath are the same directory entry,
  168. // then return no error, and perform no further action.
  169. return nil
  170. }
  171. switch {
  172. case file.IsDir():
  173. // IEEE 1003.1: if oldpath is a directory, and newpath exists,
  174. // then newpath must be a directory, and empty.
  175. // It is to be removed prior to rename.
  176. if err := fs.rmdir(newpath); err != nil {
  177. return err
  178. }
  179. case target.IsDir():
  180. // IEEE 1003.1: if oldpath is not a directory, and newpath exists,
  181. // then newpath may not be a directory.
  182. return syscall.EISDIR
  183. }
  184. }
  185. fs.files[newpath] = file
  186. if file.IsDir() {
  187. dirprefix := file.name + "/"
  188. for name, file := range fs.files {
  189. if strings.HasPrefix(name, dirprefix) {
  190. newname := path.Join(newpath, strings.TrimPrefix(name, dirprefix))
  191. fs.files[newname] = file
  192. file.name = newname
  193. delete(fs.files, name)
  194. }
  195. }
  196. }
  197. file.name = newpath
  198. delete(fs.files, oldpath)
  199. return nil
  200. }
  201. func (fs *root) PosixRename(r *Request) error {
  202. if fs.mockErr != nil {
  203. return fs.mockErr
  204. }
  205. _ = r.WithContext(r.Context()) // initialize context for deadlock testing
  206. fs.mu.Lock()
  207. defer fs.mu.Unlock()
  208. return fs.rename(r.Filepath, r.Target)
  209. }
  210. func (fs *root) StatVFS(r *Request) (*StatVFS, error) {
  211. if fs.mockErr != nil {
  212. return nil, fs.mockErr
  213. }
  214. return getStatVFSForPath(r.Filepath)
  215. }
  216. func (fs *root) mkdir(pathname string) error {
  217. dir := &memFile{
  218. modtime: time.Now(),
  219. isdir: true,
  220. }
  221. return fs.putfile(pathname, dir)
  222. }
  223. func (fs *root) rmdir(pathname string) error {
  224. // IEEE 1003.1: If pathname is a symlink, then rmdir should fail with ENOTDIR.
  225. dir, err := fs.lfetch(pathname)
  226. if err != nil {
  227. return err
  228. }
  229. if !dir.IsDir() {
  230. return syscall.ENOTDIR
  231. }
  232. // use the dir‘s internal name not the pathname we passed in.
  233. // the dir.name is always the canonical name of a directory.
  234. pathname = dir.name
  235. for name := range fs.files {
  236. if path.Dir(name) == pathname {
  237. return errors.New("directory not empty")
  238. }
  239. }
  240. delete(fs.files, pathname)
  241. return nil
  242. }
  243. func (fs *root) link(oldpath, newpath string) error {
  244. file, err := fs.lfetch(oldpath)
  245. if err != nil {
  246. return err
  247. }
  248. if file.IsDir() {
  249. return errors.New("hard link not allowed for directory")
  250. }
  251. return fs.putfile(newpath, file)
  252. }
  253. // symlink() creates a symbolic link named `linkpath` which contains the string `target`.
  254. // NOTE! This would be called with `symlink(req.Filepath, req.Target)` due to different semantics.
  255. func (fs *root) symlink(target, linkpath string) error {
  256. link := &memFile{
  257. modtime: time.Now(),
  258. symlink: target,
  259. }
  260. return fs.putfile(linkpath, link)
  261. }
  262. func (fs *root) unlink(pathname string) error {
  263. // does not follow symlinks!
  264. file, err := fs.lfetch(pathname)
  265. if err != nil {
  266. return err
  267. }
  268. if file.IsDir() {
  269. // IEEE 1003.1: implementations may opt out of allowing the unlinking of directories.
  270. // SFTP-v2: SSH_FXP_REMOVE may not remove directories.
  271. return os.ErrInvalid
  272. }
  273. // DO NOT use the file’s internal name.
  274. // because of hard-links files cannot have a single canonical name.
  275. delete(fs.files, pathname)
  276. return nil
  277. }
  278. type listerat []os.FileInfo
  279. // Modeled after strings.Reader's ReadAt() implementation
  280. func (f listerat) ListAt(ls []os.FileInfo, offset int64) (int, error) {
  281. var n int
  282. if offset >= int64(len(f)) {
  283. return 0, io.EOF
  284. }
  285. n = copy(ls, f[offset:])
  286. if n < len(ls) {
  287. return n, io.EOF
  288. }
  289. return n, nil
  290. }
  291. func (fs *root) Filelist(r *Request) (ListerAt, error) {
  292. if fs.mockErr != nil {
  293. return nil, fs.mockErr
  294. }
  295. _ = r.WithContext(r.Context()) // initialize context for deadlock testing
  296. fs.mu.Lock()
  297. defer fs.mu.Unlock()
  298. switch r.Method {
  299. case "List":
  300. files, err := fs.readdir(r.Filepath)
  301. if err != nil {
  302. return nil, err
  303. }
  304. return listerat(files), nil
  305. case "Stat":
  306. file, err := fs.fetch(r.Filepath)
  307. if err != nil {
  308. return nil, err
  309. }
  310. return listerat{file}, nil
  311. }
  312. return nil, errors.New("unsupported")
  313. }
  314. func (fs *root) readdir(pathname string) ([]os.FileInfo, error) {
  315. dir, err := fs.fetch(pathname)
  316. if err != nil {
  317. return nil, err
  318. }
  319. if !dir.IsDir() {
  320. return nil, syscall.ENOTDIR
  321. }
  322. var files []os.FileInfo
  323. for name, file := range fs.files {
  324. if path.Dir(name) == dir.name {
  325. files = append(files, file)
  326. }
  327. }
  328. sort.Slice(files, func(i, j int) bool { return files[i].Name() < files[j].Name() })
  329. return files, nil
  330. }
  331. func (fs *root) Readlink(pathname string) (string, error) {
  332. file, err := fs.lfetch(pathname)
  333. if err != nil {
  334. return "", err
  335. }
  336. if file.symlink == "" {
  337. return "", os.ErrInvalid
  338. }
  339. return file.symlink, nil
  340. }
  341. // implements LstatFileLister interface
  342. func (fs *root) Lstat(r *Request) (ListerAt, error) {
  343. if fs.mockErr != nil {
  344. return nil, fs.mockErr
  345. }
  346. _ = r.WithContext(r.Context()) // initialize context for deadlock testing
  347. fs.mu.Lock()
  348. defer fs.mu.Unlock()
  349. file, err := fs.lfetch(r.Filepath)
  350. if err != nil {
  351. return nil, err
  352. }
  353. return listerat{file}, nil
  354. }
  355. // In memory file-system-y thing that the Hanlders live on
  356. type root struct {
  357. rootFile *memFile
  358. mockErr error
  359. mu sync.Mutex
  360. files map[string]*memFile
  361. }
  362. // Set a mocked error that the next handler call will return.
  363. // Set to nil to reset for no error.
  364. func (fs *root) returnErr(err error) {
  365. fs.mockErr = err
  366. }
  367. func (fs *root) lfetch(path string) (*memFile, error) {
  368. if path == "/" {
  369. return fs.rootFile, nil
  370. }
  371. file, ok := fs.files[path]
  372. if file == nil {
  373. if ok {
  374. delete(fs.files, path)
  375. }
  376. return nil, os.ErrNotExist
  377. }
  378. return file, nil
  379. }
  380. // canonName returns the “canonical” name of a file, that is:
  381. // if the directory of the pathname is a symlink, it follows that symlink to the valid directory name.
  382. // this is relatively easy, since `dir.name` will be the only valid canonical path for a directory.
  383. func (fs *root) canonName(pathname string) (string, error) {
  384. dirname, filename := path.Dir(pathname), path.Base(pathname)
  385. dir, err := fs.fetch(dirname)
  386. if err != nil {
  387. return "", err
  388. }
  389. if !dir.IsDir() {
  390. return "", syscall.ENOTDIR
  391. }
  392. return path.Join(dir.name, filename), nil
  393. }
  394. func (fs *root) exists(path string) bool {
  395. path, err := fs.canonName(path)
  396. if err != nil {
  397. return false
  398. }
  399. _, err = fs.lfetch(path)
  400. return err != os.ErrNotExist
  401. }
  402. func (fs *root) fetch(pathname string) (*memFile, error) {
  403. file, err := fs.lfetch(pathname)
  404. if err != nil {
  405. return nil, err
  406. }
  407. var count int
  408. for file.symlink != "" {
  409. if count++; count > maxSymlinkFollows {
  410. return nil, errTooManySymlinks
  411. }
  412. linkTarget := file.symlink
  413. if !path.IsAbs(linkTarget) {
  414. linkTarget = path.Join(path.Dir(file.name), linkTarget)
  415. }
  416. file, err = fs.lfetch(linkTarget)
  417. if err != nil {
  418. return nil, err
  419. }
  420. }
  421. return file, nil
  422. }
  423. // Implements os.FileInfo, io.ReaderAt and io.WriterAt interfaces.
  424. // These are the 3 interfaces necessary for the Handlers.
  425. // Implements the optional interface TransferError.
  426. type memFile struct {
  427. name string
  428. modtime time.Time
  429. symlink string
  430. isdir bool
  431. mu sync.RWMutex
  432. content []byte
  433. err error
  434. }
  435. // These are helper functions, they must be called while holding the memFile.mu mutex
  436. func (f *memFile) size() int64 { return int64(len(f.content)) }
  437. func (f *memFile) grow(n int64) { f.content = append(f.content, make([]byte, n)...) }
  438. // Have memFile fulfill os.FileInfo interface
  439. func (f *memFile) Name() string { return path.Base(f.name) }
  440. func (f *memFile) Size() int64 {
  441. f.mu.Lock()
  442. defer f.mu.Unlock()
  443. return f.size()
  444. }
  445. func (f *memFile) Mode() os.FileMode {
  446. if f.isdir {
  447. return os.FileMode(0755) | os.ModeDir
  448. }
  449. if f.symlink != "" {
  450. return os.FileMode(0777) | os.ModeSymlink
  451. }
  452. return os.FileMode(0644)
  453. }
  454. func (f *memFile) ModTime() time.Time { return f.modtime }
  455. func (f *memFile) IsDir() bool { return f.isdir }
  456. func (f *memFile) Sys() interface{} {
  457. return fakeFileInfoSys()
  458. }
  459. func (f *memFile) ReadAt(b []byte, off int64) (int, error) {
  460. f.mu.Lock()
  461. defer f.mu.Unlock()
  462. if f.err != nil {
  463. return 0, f.err
  464. }
  465. if off < 0 {
  466. return 0, errors.New("memFile.ReadAt: negative offset")
  467. }
  468. if off >= f.size() {
  469. return 0, io.EOF
  470. }
  471. n := copy(b, f.content[off:])
  472. if n < len(b) {
  473. return n, io.EOF
  474. }
  475. return n, nil
  476. }
  477. func (f *memFile) WriteAt(b []byte, off int64) (int, error) {
  478. // fmt.Println(string(p), off)
  479. // mimic write delays, should be optional
  480. time.Sleep(time.Microsecond * time.Duration(len(b)))
  481. f.mu.Lock()
  482. defer f.mu.Unlock()
  483. if f.err != nil {
  484. return 0, f.err
  485. }
  486. grow := int64(len(b)) + off - f.size()
  487. if grow > 0 {
  488. f.grow(grow)
  489. }
  490. return copy(f.content[off:], b), nil
  491. }
  492. func (f *memFile) Truncate(size int64) error {
  493. f.mu.Lock()
  494. defer f.mu.Unlock()
  495. if f.err != nil {
  496. return f.err
  497. }
  498. grow := size - f.size()
  499. if grow <= 0 {
  500. f.content = f.content[:size]
  501. } else {
  502. f.grow(grow)
  503. }
  504. return nil
  505. }
  506. func (f *memFile) TransferError(err error) {
  507. f.mu.Lock()
  508. defer f.mu.Unlock()
  509. f.err = err
  510. }