btrfs.go 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  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. static void set_name_btrfs_ioctl_vol_args_v2(struct btrfs_ioctl_vol_args_v2* btrfs_struct, const char* value) {
  9. snprintf(btrfs_struct->name, BTRFS_SUBVOL_NAME_MAX, "%s", value);
  10. }
  11. */
  12. import "C"
  13. import (
  14. "fmt"
  15. "os"
  16. "path"
  17. "path/filepath"
  18. "syscall"
  19. "unsafe"
  20. "github.com/docker/docker/daemon/graphdriver"
  21. "github.com/docker/docker/pkg/idtools"
  22. "github.com/docker/docker/pkg/mount"
  23. "github.com/opencontainers/runc/libcontainer/label"
  24. )
  25. func init() {
  26. graphdriver.Register("btrfs", Init)
  27. }
  28. // Init returns a new BTRFS driver.
  29. // An error is returned if BTRFS is not supported.
  30. func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) {
  31. fsMagic, err := graphdriver.GetFSMagic(home)
  32. if err != nil {
  33. return nil, err
  34. }
  35. if fsMagic != graphdriver.FsMagicBtrfs {
  36. return nil, graphdriver.ErrPrerequisites
  37. }
  38. rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps)
  39. if err != nil {
  40. return nil, err
  41. }
  42. if err := idtools.MkdirAllAs(home, 0700, rootUID, rootGID); err != nil {
  43. return nil, err
  44. }
  45. if err := mount.MakePrivate(home); err != nil {
  46. return nil, err
  47. }
  48. driver := &Driver{
  49. home: home,
  50. uidMaps: uidMaps,
  51. gidMaps: gidMaps,
  52. }
  53. return graphdriver.NewNaiveDiffDriver(driver, uidMaps, gidMaps), nil
  54. }
  55. // Driver contains information about the filesystem mounted.
  56. type Driver struct {
  57. //root of the file system
  58. home string
  59. uidMaps []idtools.IDMap
  60. gidMaps []idtools.IDMap
  61. }
  62. // String prints the name of the driver (btrfs).
  63. func (d *Driver) String() string {
  64. return "btrfs"
  65. }
  66. // Status returns current driver information in a two dimensional string array.
  67. // Output contains "Build Version" and "Library Version" of the btrfs libraries used.
  68. // Version information can be used to check compatibility with your kernel.
  69. func (d *Driver) Status() [][2]string {
  70. status := [][2]string{}
  71. if bv := btrfsBuildVersion(); bv != "-" {
  72. status = append(status, [2]string{"Build Version", bv})
  73. }
  74. if lv := btrfsLibVersion(); lv != -1 {
  75. status = append(status, [2]string{"Library Version", fmt.Sprintf("%d", lv)})
  76. }
  77. return status
  78. }
  79. // GetMetadata returns empty metadata for this driver.
  80. func (d *Driver) GetMetadata(id string) (map[string]string, error) {
  81. return nil, nil
  82. }
  83. // Cleanup unmounts the home directory.
  84. func (d *Driver) Cleanup() error {
  85. return mount.Unmount(d.home)
  86. }
  87. func free(p *C.char) {
  88. C.free(unsafe.Pointer(p))
  89. }
  90. func openDir(path string) (*C.DIR, error) {
  91. Cpath := C.CString(path)
  92. defer free(Cpath)
  93. dir := C.opendir(Cpath)
  94. if dir == nil {
  95. return nil, fmt.Errorf("Can't open dir")
  96. }
  97. return dir, nil
  98. }
  99. func closeDir(dir *C.DIR) {
  100. if dir != nil {
  101. C.closedir(dir)
  102. }
  103. }
  104. func getDirFd(dir *C.DIR) uintptr {
  105. return uintptr(C.dirfd(dir))
  106. }
  107. func subvolCreate(path, name string) error {
  108. dir, err := openDir(path)
  109. if err != nil {
  110. return err
  111. }
  112. defer closeDir(dir)
  113. var args C.struct_btrfs_ioctl_vol_args
  114. for i, c := range []byte(name) {
  115. args.name[i] = C.char(c)
  116. }
  117. _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, getDirFd(dir), C.BTRFS_IOC_SUBVOL_CREATE,
  118. uintptr(unsafe.Pointer(&args)))
  119. if errno != 0 {
  120. return fmt.Errorf("Failed to create btrfs subvolume: %v", errno.Error())
  121. }
  122. return nil
  123. }
  124. func subvolSnapshot(src, dest, name string) error {
  125. srcDir, err := openDir(src)
  126. if err != nil {
  127. return err
  128. }
  129. defer closeDir(srcDir)
  130. destDir, err := openDir(dest)
  131. if err != nil {
  132. return err
  133. }
  134. defer closeDir(destDir)
  135. var args C.struct_btrfs_ioctl_vol_args_v2
  136. args.fd = C.__s64(getDirFd(srcDir))
  137. var cs = C.CString(name)
  138. C.set_name_btrfs_ioctl_vol_args_v2(&args, cs)
  139. C.free(unsafe.Pointer(cs))
  140. _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, getDirFd(destDir), C.BTRFS_IOC_SNAP_CREATE_V2,
  141. uintptr(unsafe.Pointer(&args)))
  142. if errno != 0 {
  143. return fmt.Errorf("Failed to create btrfs snapshot: %v", errno.Error())
  144. }
  145. return nil
  146. }
  147. func isSubvolume(p string) (bool, error) {
  148. var bufStat syscall.Stat_t
  149. if err := syscall.Lstat(p, &bufStat); err != nil {
  150. return false, err
  151. }
  152. // return true if it is a btrfs subvolume
  153. return bufStat.Ino == C.BTRFS_FIRST_FREE_OBJECTID, nil
  154. }
  155. func subvolDelete(dirpath, name string) error {
  156. dir, err := openDir(dirpath)
  157. if err != nil {
  158. return err
  159. }
  160. defer closeDir(dir)
  161. fullPath := path.Join(dirpath, name)
  162. var args C.struct_btrfs_ioctl_vol_args
  163. // walk the btrfs subvolumes
  164. walkSubvolumes := func(p string, f os.FileInfo, err error) error {
  165. if err != nil {
  166. if os.IsNotExist(err) && p != fullPath {
  167. // missing most likely because the path was a subvolume that got removed in the previous iteration
  168. // since it's gone anyway, we don't care
  169. return nil
  170. }
  171. return fmt.Errorf("error walking subvolumes: %v", err)
  172. }
  173. // we want to check children only so skip itself
  174. // it will be removed after the filepath walk anyways
  175. if f.IsDir() && p != fullPath {
  176. sv, err := isSubvolume(p)
  177. if err != nil {
  178. return fmt.Errorf("Failed to test if %s is a btrfs subvolume: %v", p, err)
  179. }
  180. if sv {
  181. if err := subvolDelete(path.Dir(p), f.Name()); err != nil {
  182. return fmt.Errorf("Failed to destroy btrfs child subvolume (%s) of parent (%s): %v", p, dirpath, err)
  183. }
  184. }
  185. }
  186. return nil
  187. }
  188. if err := filepath.Walk(path.Join(dirpath, name), walkSubvolumes); err != nil {
  189. return fmt.Errorf("Recursively walking subvolumes for %s failed: %v", dirpath, err)
  190. }
  191. // all subvolumes have been removed
  192. // now remove the one originally passed in
  193. for i, c := range []byte(name) {
  194. args.name[i] = C.char(c)
  195. }
  196. _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, getDirFd(dir), C.BTRFS_IOC_SNAP_DESTROY,
  197. uintptr(unsafe.Pointer(&args)))
  198. if errno != 0 {
  199. return fmt.Errorf("Failed to destroy btrfs snapshot %s for %s: %v", dirpath, name, errno.Error())
  200. }
  201. return nil
  202. }
  203. func (d *Driver) subvolumesDir() string {
  204. return path.Join(d.home, "subvolumes")
  205. }
  206. func (d *Driver) subvolumesDirID(id string) string {
  207. return path.Join(d.subvolumesDir(), id)
  208. }
  209. // Create the filesystem with given id.
  210. func (d *Driver) Create(id, parent, mountLabel string, storageOpt map[string]string) error {
  211. if len(storageOpt) != 0 {
  212. return fmt.Errorf("--storage-opt is not supported for btrfs")
  213. }
  214. subvolumes := path.Join(d.home, "subvolumes")
  215. rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps)
  216. if err != nil {
  217. return err
  218. }
  219. if err := idtools.MkdirAllAs(subvolumes, 0700, rootUID, rootGID); err != nil {
  220. return err
  221. }
  222. if parent == "" {
  223. if err := subvolCreate(subvolumes, id); err != nil {
  224. return err
  225. }
  226. } else {
  227. parentDir := d.subvolumesDirID(parent)
  228. st, err := os.Stat(parentDir)
  229. if err != nil {
  230. return err
  231. }
  232. if !st.IsDir() {
  233. return fmt.Errorf("%s: not a directory", parentDir)
  234. }
  235. if err := subvolSnapshot(parentDir, subvolumes, id); err != nil {
  236. return err
  237. }
  238. }
  239. // if we have a remapped root (user namespaces enabled), change the created snapshot
  240. // dir ownership to match
  241. if rootUID != 0 || rootGID != 0 {
  242. if err := os.Chown(path.Join(subvolumes, id), rootUID, rootGID); err != nil {
  243. return err
  244. }
  245. }
  246. return label.Relabel(path.Join(subvolumes, id), mountLabel, false)
  247. }
  248. // Remove the filesystem with given id.
  249. func (d *Driver) Remove(id string) error {
  250. dir := d.subvolumesDirID(id)
  251. if _, err := os.Stat(dir); err != nil {
  252. return err
  253. }
  254. if err := subvolDelete(d.subvolumesDir(), id); err != nil {
  255. return err
  256. }
  257. if err := os.RemoveAll(dir); err != nil && !os.IsNotExist(err) {
  258. return err
  259. }
  260. return nil
  261. }
  262. // Get the requested filesystem id.
  263. func (d *Driver) Get(id, mountLabel string) (string, error) {
  264. dir := d.subvolumesDirID(id)
  265. st, err := os.Stat(dir)
  266. if err != nil {
  267. return "", err
  268. }
  269. if !st.IsDir() {
  270. return "", fmt.Errorf("%s: not a directory", dir)
  271. }
  272. return dir, nil
  273. }
  274. // Put is not implemented for BTRFS as there is no cleanup required for the id.
  275. func (d *Driver) Put(id string) error {
  276. // Get() creates no runtime resources (like e.g. mounts)
  277. // so this doesn't need to do anything.
  278. return nil
  279. }
  280. // Exists checks if the id exists in the filesystem.
  281. func (d *Driver) Exists(id string) bool {
  282. dir := d.subvolumesDirID(id)
  283. _, err := os.Stat(dir)
  284. return err == nil
  285. }