zfs.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  1. // Package zfs provides wrappers around the ZFS command line tools.
  2. package zfs
  3. import (
  4. "errors"
  5. "fmt"
  6. "io"
  7. "strconv"
  8. "strings"
  9. )
  10. // ZFS dataset types, which can indicate if a dataset is a filesystem,
  11. // snapshot, or volume.
  12. const (
  13. DatasetFilesystem = "filesystem"
  14. DatasetSnapshot = "snapshot"
  15. DatasetVolume = "volume"
  16. )
  17. // Dataset is a ZFS dataset. A dataset could be a clone, filesystem, snapshot,
  18. // or volume. The Type struct member can be used to determine a dataset's type.
  19. //
  20. // The field definitions can be found in the ZFS manual:
  21. // http://www.freebsd.org/cgi/man.cgi?zfs(8).
  22. type Dataset struct {
  23. Name string
  24. Origin string
  25. Used uint64
  26. Avail uint64
  27. Mountpoint string
  28. Compression string
  29. Type string
  30. Written uint64
  31. Volsize uint64
  32. Logicalused uint64
  33. Usedbydataset uint64
  34. Quota uint64
  35. }
  36. // InodeType is the type of inode as reported by Diff
  37. type InodeType int
  38. // Types of Inodes
  39. const (
  40. _ = iota // 0 == unknown type
  41. BlockDevice InodeType = iota
  42. CharacterDevice
  43. Directory
  44. Door
  45. NamedPipe
  46. SymbolicLink
  47. EventPort
  48. Socket
  49. File
  50. )
  51. // ChangeType is the type of inode change as reported by Diff
  52. type ChangeType int
  53. // Types of Changes
  54. const (
  55. _ = iota // 0 == unknown type
  56. Removed ChangeType = iota
  57. Created
  58. Modified
  59. Renamed
  60. )
  61. // DestroyFlag is the options flag passed to Destroy
  62. type DestroyFlag int
  63. // Valid destroy options
  64. const (
  65. DestroyDefault DestroyFlag = 1 << iota
  66. DestroyRecursive = 1 << iota
  67. DestroyRecursiveClones = 1 << iota
  68. DestroyDeferDeletion = 1 << iota
  69. DestroyForceUmount = 1 << iota
  70. )
  71. // InodeChange represents a change as reported by Diff
  72. type InodeChange struct {
  73. Change ChangeType
  74. Type InodeType
  75. Path string
  76. NewPath string
  77. ReferenceCountChange int
  78. }
  79. // Logger can be used to log commands/actions
  80. type Logger interface {
  81. Log(cmd []string)
  82. }
  83. type defaultLogger struct{}
  84. func (*defaultLogger) Log(cmd []string) {
  85. return
  86. }
  87. var logger Logger = &defaultLogger{}
  88. // SetLogger set a log handler to log all commands including arguments before
  89. // they are executed
  90. func SetLogger(l Logger) {
  91. if l != nil {
  92. logger = l
  93. }
  94. }
  95. // zfs is a helper function to wrap typical calls to zfs.
  96. func zfs(arg ...string) ([][]string, error) {
  97. c := command{Command: "zfs"}
  98. return c.Run(arg...)
  99. }
  100. // Datasets returns a slice of ZFS datasets, regardless of type.
  101. // A filter argument may be passed to select a dataset with the matching name,
  102. // or empty string ("") may be used to select all datasets.
  103. func Datasets(filter string) ([]*Dataset, error) {
  104. return listByType("all", filter)
  105. }
  106. // Snapshots returns a slice of ZFS snapshots.
  107. // A filter argument may be passed to select a snapshot with the matching name,
  108. // or empty string ("") may be used to select all snapshots.
  109. func Snapshots(filter string) ([]*Dataset, error) {
  110. return listByType(DatasetSnapshot, filter)
  111. }
  112. // Filesystems returns a slice of ZFS filesystems.
  113. // A filter argument may be passed to select a filesystem with the matching name,
  114. // or empty string ("") may be used to select all filesystems.
  115. func Filesystems(filter string) ([]*Dataset, error) {
  116. return listByType(DatasetFilesystem, filter)
  117. }
  118. // Volumes returns a slice of ZFS volumes.
  119. // A filter argument may be passed to select a volume with the matching name,
  120. // or empty string ("") may be used to select all volumes.
  121. func Volumes(filter string) ([]*Dataset, error) {
  122. return listByType(DatasetVolume, filter)
  123. }
  124. // GetDataset retrieves a single ZFS dataset by name. This dataset could be
  125. // any valid ZFS dataset type, such as a clone, filesystem, snapshot, or volume.
  126. func GetDataset(name string) (*Dataset, error) {
  127. out, err := zfs("list", "-Hp", "-o", dsPropListOptions, name)
  128. if err != nil {
  129. return nil, err
  130. }
  131. ds := &Dataset{Name: name}
  132. for _, line := range out {
  133. if err := ds.parseLine(line); err != nil {
  134. return nil, err
  135. }
  136. }
  137. return ds, nil
  138. }
  139. // Clone clones a ZFS snapshot and returns a clone dataset.
  140. // An error will be returned if the input dataset is not of snapshot type.
  141. func (d *Dataset) Clone(dest string, properties map[string]string) (*Dataset, error) {
  142. if d.Type != DatasetSnapshot {
  143. return nil, errors.New("can only clone snapshots")
  144. }
  145. args := make([]string, 2, 4)
  146. args[0] = "clone"
  147. args[1] = "-p"
  148. if properties != nil {
  149. args = append(args, propsSlice(properties)...)
  150. }
  151. args = append(args, []string{d.Name, dest}...)
  152. _, err := zfs(args...)
  153. if err != nil {
  154. return nil, err
  155. }
  156. return GetDataset(dest)
  157. }
  158. // Unmount unmounts currently mounted ZFS file systems.
  159. func (d *Dataset) Unmount(force bool) (*Dataset, error) {
  160. if d.Type == DatasetSnapshot {
  161. return nil, errors.New("cannot unmount snapshots")
  162. }
  163. args := make([]string, 1, 3)
  164. args[0] = "umount"
  165. if force {
  166. args = append(args, "-f")
  167. }
  168. args = append(args, d.Name)
  169. _, err := zfs(args...)
  170. if err != nil {
  171. return nil, err
  172. }
  173. return GetDataset(d.Name)
  174. }
  175. // Mount mounts ZFS file systems.
  176. func (d *Dataset) Mount(overlay bool, options []string) (*Dataset, error) {
  177. if d.Type == DatasetSnapshot {
  178. return nil, errors.New("cannot mount snapshots")
  179. }
  180. args := make([]string, 1, 5)
  181. args[0] = "mount"
  182. if overlay {
  183. args = append(args, "-O")
  184. }
  185. if options != nil {
  186. args = append(args, "-o")
  187. args = append(args, strings.Join(options, ","))
  188. }
  189. args = append(args, d.Name)
  190. _, err := zfs(args...)
  191. if err != nil {
  192. return nil, err
  193. }
  194. return GetDataset(d.Name)
  195. }
  196. // ReceiveSnapshot receives a ZFS stream from the input io.Reader, creates a
  197. // new snapshot with the specified name, and streams the input data into the
  198. // newly-created snapshot.
  199. func ReceiveSnapshot(input io.Reader, name string) (*Dataset, error) {
  200. c := command{Command: "zfs", Stdin: input}
  201. _, err := c.Run("receive", name)
  202. if err != nil {
  203. return nil, err
  204. }
  205. return GetDataset(name)
  206. }
  207. // SendSnapshot sends a ZFS stream of a snapshot to the input io.Writer.
  208. // An error will be returned if the input dataset is not of snapshot type.
  209. func (d *Dataset) SendSnapshot(output io.Writer) error {
  210. if d.Type != DatasetSnapshot {
  211. return errors.New("can only send snapshots")
  212. }
  213. c := command{Command: "zfs", Stdout: output}
  214. _, err := c.Run("send", d.Name)
  215. return err
  216. }
  217. // CreateVolume creates a new ZFS volume with the specified name, size, and
  218. // properties.
  219. // A full list of available ZFS properties may be found here:
  220. // https://www.freebsd.org/cgi/man.cgi?zfs(8).
  221. func CreateVolume(name string, size uint64, properties map[string]string) (*Dataset, error) {
  222. args := make([]string, 4, 5)
  223. args[0] = "create"
  224. args[1] = "-p"
  225. args[2] = "-V"
  226. args[3] = strconv.FormatUint(size, 10)
  227. if properties != nil {
  228. args = append(args, propsSlice(properties)...)
  229. }
  230. args = append(args, name)
  231. _, err := zfs(args...)
  232. if err != nil {
  233. return nil, err
  234. }
  235. return GetDataset(name)
  236. }
  237. // Destroy destroys a ZFS dataset. If the destroy bit flag is set, any
  238. // descendents of the dataset will be recursively destroyed, including snapshots.
  239. // If the deferred bit flag is set, the snapshot is marked for deferred
  240. // deletion.
  241. func (d *Dataset) Destroy(flags DestroyFlag) error {
  242. args := make([]string, 1, 3)
  243. args[0] = "destroy"
  244. if flags&DestroyRecursive != 0 {
  245. args = append(args, "-r")
  246. }
  247. if flags&DestroyRecursiveClones != 0 {
  248. args = append(args, "-R")
  249. }
  250. if flags&DestroyDeferDeletion != 0 {
  251. args = append(args, "-d")
  252. }
  253. if flags&DestroyForceUmount != 0 {
  254. args = append(args, "-f")
  255. }
  256. args = append(args, d.Name)
  257. _, err := zfs(args...)
  258. return err
  259. }
  260. // SetProperty sets a ZFS property on the receiving dataset.
  261. // A full list of available ZFS properties may be found here:
  262. // https://www.freebsd.org/cgi/man.cgi?zfs(8).
  263. func (d *Dataset) SetProperty(key, val string) error {
  264. prop := strings.Join([]string{key, val}, "=")
  265. _, err := zfs("set", prop, d.Name)
  266. return err
  267. }
  268. // GetProperty returns the current value of a ZFS property from the
  269. // receiving dataset.
  270. // A full list of available ZFS properties may be found here:
  271. // https://www.freebsd.org/cgi/man.cgi?zfs(8).
  272. func (d *Dataset) GetProperty(key string) (string, error) {
  273. out, err := zfs("get", key, d.Name)
  274. if err != nil {
  275. return "", err
  276. }
  277. return out[0][2], nil
  278. }
  279. // Rename renames a dataset.
  280. func (d *Dataset) Rename(name string, createParent bool, recursiveRenameSnapshots bool) (*Dataset, error) {
  281. args := make([]string, 3, 5)
  282. args[0] = "rename"
  283. args[1] = d.Name
  284. args[2] = name
  285. if createParent {
  286. args = append(args, "-p")
  287. }
  288. if recursiveRenameSnapshots {
  289. args = append(args, "-r")
  290. }
  291. _, err := zfs(args...)
  292. if err != nil {
  293. return d, err
  294. }
  295. return GetDataset(name)
  296. }
  297. // Snapshots returns a slice of all ZFS snapshots of a given dataset.
  298. func (d *Dataset) Snapshots() ([]*Dataset, error) {
  299. return Snapshots(d.Name)
  300. }
  301. // CreateFilesystem creates a new ZFS filesystem with the specified name and
  302. // properties.
  303. // A full list of available ZFS properties may be found here:
  304. // https://www.freebsd.org/cgi/man.cgi?zfs(8).
  305. func CreateFilesystem(name string, properties map[string]string) (*Dataset, error) {
  306. args := make([]string, 1, 4)
  307. args[0] = "create"
  308. if properties != nil {
  309. args = append(args, propsSlice(properties)...)
  310. }
  311. args = append(args, name)
  312. _, err := zfs(args...)
  313. if err != nil {
  314. return nil, err
  315. }
  316. return GetDataset(name)
  317. }
  318. // Snapshot creates a new ZFS snapshot of the receiving dataset, using the
  319. // specified name. Optionally, the snapshot can be taken recursively, creating
  320. // snapshots of all descendent filesystems in a single, atomic operation.
  321. func (d *Dataset) Snapshot(name string, recursive bool) (*Dataset, error) {
  322. args := make([]string, 1, 4)
  323. args[0] = "snapshot"
  324. if recursive {
  325. args = append(args, "-r")
  326. }
  327. snapName := fmt.Sprintf("%s@%s", d.Name, name)
  328. args = append(args, snapName)
  329. _, err := zfs(args...)
  330. if err != nil {
  331. return nil, err
  332. }
  333. return GetDataset(snapName)
  334. }
  335. // Rollback rolls back the receiving ZFS dataset to a previous snapshot.
  336. // Optionally, intermediate snapshots can be destroyed. A ZFS snapshot
  337. // rollback cannot be completed without this option, if more recent
  338. // snapshots exist.
  339. // An error will be returned if the input dataset is not of snapshot type.
  340. func (d *Dataset) Rollback(destroyMoreRecent bool) error {
  341. if d.Type != DatasetSnapshot {
  342. return errors.New("can only rollback snapshots")
  343. }
  344. args := make([]string, 1, 3)
  345. args[0] = "rollback"
  346. if destroyMoreRecent {
  347. args = append(args, "-r")
  348. }
  349. args = append(args, d.Name)
  350. _, err := zfs(args...)
  351. return err
  352. }
  353. // Children returns a slice of children of the receiving ZFS dataset.
  354. // A recursion depth may be specified, or a depth of 0 allows unlimited
  355. // recursion.
  356. func (d *Dataset) Children(depth uint64) ([]*Dataset, error) {
  357. args := []string{"list"}
  358. if depth > 0 {
  359. args = append(args, "-d")
  360. args = append(args, strconv.FormatUint(depth, 10))
  361. } else {
  362. args = append(args, "-r")
  363. }
  364. args = append(args, "-t", "all", "-Hp", "-o", dsPropListOptions)
  365. args = append(args, d.Name)
  366. out, err := zfs(args...)
  367. if err != nil {
  368. return nil, err
  369. }
  370. var datasets []*Dataset
  371. name := ""
  372. var ds *Dataset
  373. for _, line := range out {
  374. if name != line[0] {
  375. name = line[0]
  376. ds = &Dataset{Name: name}
  377. datasets = append(datasets, ds)
  378. }
  379. if err := ds.parseLine(line); err != nil {
  380. return nil, err
  381. }
  382. }
  383. return datasets[1:], nil
  384. }
  385. // Diff returns changes between a snapshot and the given ZFS dataset.
  386. // The snapshot name must include the filesystem part as it is possible to
  387. // compare clones with their origin snapshots.
  388. func (d *Dataset) Diff(snapshot string) ([]*InodeChange, error) {
  389. args := []string{"diff", "-FH", snapshot, d.Name}[:]
  390. out, err := zfs(args...)
  391. if err != nil {
  392. return nil, err
  393. }
  394. inodeChanges, err := parseInodeChanges(out)
  395. if err != nil {
  396. return nil, err
  397. }
  398. return inodeChanges, nil
  399. }