btrfs.go 5.4 KB

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