btrfs.go 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. // +build linux
  2. package btrfs
  3. /*
  4. #include <stdlib.h>
  5. #include <dirent.h>
  6. #include <btrfs/ioctl.h>
  7. #include <btrfs/ctree.h>
  8. */
  9. import "C"
  10. import (
  11. "fmt"
  12. "os"
  13. "path"
  14. "path/filepath"
  15. "syscall"
  16. "unsafe"
  17. "github.com/docker/docker/daemon/graphdriver"
  18. "github.com/docker/docker/pkg/mount"
  19. )
  20. func init() {
  21. graphdriver.Register("btrfs", Init)
  22. }
  23. // Init returns a new BTRFS driver.
  24. // An error is returned if BTRFS is not supported.
  25. func Init(home string, options []string) (graphdriver.Driver, error) {
  26. rootdir := path.Dir(home)
  27. var buf syscall.Statfs_t
  28. if err := syscall.Statfs(rootdir, &buf); err != nil {
  29. return nil, err
  30. }
  31. if graphdriver.FsMagic(buf.Type) != graphdriver.FsMagicBtrfs {
  32. return nil, graphdriver.ErrPrerequisites
  33. }
  34. if err := os.MkdirAll(home, 0700); err != nil {
  35. return nil, err
  36. }
  37. if err := mount.MakePrivate(home); err != nil {
  38. return nil, err
  39. }
  40. driver := &Driver{
  41. home: home,
  42. }
  43. return graphdriver.NewNaiveDiffDriver(driver), nil
  44. }
  45. // Driver contains information about the filesystem mounted.
  46. type Driver struct {
  47. //root of the file system
  48. home string
  49. }
  50. // String prints the name of the driver (btrfs).
  51. func (d *Driver) String() string {
  52. return "btrfs"
  53. }
  54. // Status returns current driver information in a two dimensional string array.
  55. // Output contains "Build Version" and "Library Version" of the btrfs libraries used.
  56. // Version information can be used to check compatibility with your kernel.
  57. func (d *Driver) Status() [][2]string {
  58. status := [][2]string{}
  59. if bv := btrfsBuildVersion(); bv != "-" {
  60. status = append(status, [2]string{"Build Version", bv})
  61. }
  62. if lv := btrfsLibVersion(); lv != -1 {
  63. status = append(status, [2]string{"Library Version", fmt.Sprintf("%d", lv)})
  64. }
  65. return status
  66. }
  67. // GetMetadata returns empty metadata for this driver.
  68. func (d *Driver) GetMetadata(id string) (map[string]string, error) {
  69. return nil, nil
  70. }
  71. // Cleanup unmounts the home directory.
  72. func (d *Driver) Cleanup() error {
  73. return mount.Unmount(d.home)
  74. }
  75. func free(p *C.char) {
  76. C.free(unsafe.Pointer(p))
  77. }
  78. func openDir(path string) (*C.DIR, error) {
  79. Cpath := C.CString(path)
  80. defer free(Cpath)
  81. dir := C.opendir(Cpath)
  82. if dir == nil {
  83. return nil, fmt.Errorf("Can't open dir")
  84. }
  85. return dir, nil
  86. }
  87. func closeDir(dir *C.DIR) {
  88. if dir != nil {
  89. C.closedir(dir)
  90. }
  91. }
  92. func getDirFd(dir *C.DIR) uintptr {
  93. return uintptr(C.dirfd(dir))
  94. }
  95. func subvolCreate(path, name string) error {
  96. dir, err := openDir(path)
  97. if err != nil {
  98. return err
  99. }
  100. defer closeDir(dir)
  101. var args C.struct_btrfs_ioctl_vol_args
  102. for i, c := range []byte(name) {
  103. args.name[i] = C.char(c)
  104. }
  105. _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, getDirFd(dir), C.BTRFS_IOC_SUBVOL_CREATE,
  106. uintptr(unsafe.Pointer(&args)))
  107. if errno != 0 {
  108. return fmt.Errorf("Failed to create btrfs subvolume: %v", errno.Error())
  109. }
  110. return nil
  111. }
  112. func subvolSnapshot(src, dest, name string) error {
  113. srcDir, err := openDir(src)
  114. if err != nil {
  115. return err
  116. }
  117. defer closeDir(srcDir)
  118. destDir, err := openDir(dest)
  119. if err != nil {
  120. return err
  121. }
  122. defer closeDir(destDir)
  123. var args C.struct_btrfs_ioctl_vol_args_v2
  124. args.fd = C.__s64(getDirFd(srcDir))
  125. for i, c := range []byte(name) {
  126. args.name[i] = C.char(c)
  127. }
  128. _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, getDirFd(destDir), C.BTRFS_IOC_SNAP_CREATE_V2,
  129. uintptr(unsafe.Pointer(&args)))
  130. if errno != 0 {
  131. return fmt.Errorf("Failed to create btrfs snapshot: %v", errno.Error())
  132. }
  133. return nil
  134. }
  135. func isSubvolume(p string) (bool, error) {
  136. var bufStat syscall.Stat_t
  137. if err := syscall.Lstat(p, &bufStat); err != nil {
  138. return false, err
  139. }
  140. // return true if it is a btrfs subvolume
  141. return bufStat.Ino == C.BTRFS_FIRST_FREE_OBJECTID, nil
  142. }
  143. func subvolDelete(dirpath, name string) error {
  144. dir, err := openDir(dirpath)
  145. if err != nil {
  146. return err
  147. }
  148. defer closeDir(dir)
  149. var args C.struct_btrfs_ioctl_vol_args
  150. // walk the btrfs subvolumes
  151. walkSubvolumes := func(p string, f os.FileInfo, err error) error {
  152. // we want to check children only so skip itself
  153. // it will be removed after the filepath walk anyways
  154. if f.IsDir() && p != path.Join(dirpath, name) {
  155. sv, err := isSubvolume(p)
  156. if err != nil {
  157. return fmt.Errorf("Failed to test if %s is a btrfs subvolume: %v", p, err)
  158. }
  159. if sv {
  160. if err := subvolDelete(p, f.Name()); err != nil {
  161. return fmt.Errorf("Failed to destroy btrfs child subvolume (%s) of parent (%s): %v", p, dirpath, err)
  162. }
  163. }
  164. }
  165. return nil
  166. }
  167. if err := filepath.Walk(path.Join(dirpath, name), walkSubvolumes); err != nil {
  168. return fmt.Errorf("Recursively walking subvolumes for %s failed: %v", dirpath, err)
  169. }
  170. // all subvolumes have been removed
  171. // now remove the one originally passed in
  172. for i, c := range []byte(name) {
  173. args.name[i] = C.char(c)
  174. }
  175. _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, getDirFd(dir), C.BTRFS_IOC_SNAP_DESTROY,
  176. uintptr(unsafe.Pointer(&args)))
  177. if errno != 0 {
  178. return fmt.Errorf("Failed to destroy btrfs snapshot %s for %s: %v", dirpath, name, errno.Error())
  179. }
  180. return nil
  181. }
  182. func (d *Driver) subvolumesDir() string {
  183. return path.Join(d.home, "subvolumes")
  184. }
  185. func (d *Driver) subvolumesDirID(id string) string {
  186. return path.Join(d.subvolumesDir(), id)
  187. }
  188. // Create the filesystem with given id.
  189. func (d *Driver) Create(id string, parent string) error {
  190. subvolumes := path.Join(d.home, "subvolumes")
  191. if err := os.MkdirAll(subvolumes, 0700); err != nil {
  192. return err
  193. }
  194. if parent == "" {
  195. if err := subvolCreate(subvolumes, id); err != nil {
  196. return err
  197. }
  198. } else {
  199. parentDir, err := d.Get(parent, "")
  200. if err != nil {
  201. return err
  202. }
  203. if err := subvolSnapshot(parentDir, subvolumes, id); err != nil {
  204. return err
  205. }
  206. }
  207. return nil
  208. }
  209. // Remove the filesystem with given id.
  210. func (d *Driver) Remove(id string) error {
  211. dir := d.subvolumesDirID(id)
  212. if _, err := os.Stat(dir); err != nil {
  213. return err
  214. }
  215. if err := subvolDelete(d.subvolumesDir(), id); err != nil {
  216. return err
  217. }
  218. return os.RemoveAll(dir)
  219. }
  220. // Get the requested filesystem id.
  221. func (d *Driver) Get(id, mountLabel string) (string, error) {
  222. dir := d.subvolumesDirID(id)
  223. st, err := os.Stat(dir)
  224. if err != nil {
  225. return "", err
  226. }
  227. if !st.IsDir() {
  228. return "", fmt.Errorf("%s: not a directory", dir)
  229. }
  230. return dir, nil
  231. }
  232. // Put is not implemented for BTRFS as there is no cleanup required for the id.
  233. func (d *Driver) Put(id string) error {
  234. // Get() creates no runtime resources (like e.g. mounts)
  235. // so this doesn't need to do anything.
  236. return nil
  237. }
  238. // Exists checks if the id exists in the filesystem.
  239. func (d *Driver) Exists(id string) bool {
  240. dir := d.subvolumesDirID(id)
  241. _, err := os.Stat(dir)
  242. return err == nil
  243. }