implement subvolumes list; regenerate headers; generate btrfs_tree.h

This commit is contained in:
Denys Smirnov 2016-09-16 20:09:14 +03:00
parent b300237e77
commit f03fa748e7
10 changed files with 1047 additions and 44 deletions

View File

@ -2,6 +2,8 @@ package btrfs
import "strings"
const maxUint64 = 1<<64 - 1
const BTRFS_LABEL_SIZE = 256
type FeatureFlags uint64

54
btrfs_tree.go Normal file
View File

@ -0,0 +1,54 @@
package btrfs
import (
"time"
"unsafe"
)
const (
_BTRFS_BLOCK_GROUP_PROFILE_MASK = (blockGroupRaid0 |
blockGroupRaid1 |
blockGroupRaid5 |
blockGroupRaid6 |
blockGroupDup |
blockGroupRaid10)
)
type rootRef struct {
DirID uint64
Sequence uint64
Name string
}
func (rootRef) btrfsSize() int { return 18 }
func asUint64(p []byte) uint64 {
return *(*uint64)(unsafe.Pointer(&p[0]))
}
func asUint32(p []byte) uint32 {
return *(*uint32)(unsafe.Pointer(&p[0]))
}
func asUint16(p []byte) uint16 {
return *(*uint16)(unsafe.Pointer(&p[0]))
}
func asTime(p []byte) time.Time {
sec, nsec := asUint64(p[0:]), asUint32(p[8:])
return time.Unix(int64(sec), int64(nsec))
}
func asRootRef(p []byte) rootRef {
const sz = 18
// assuming that it is highly unsafe to have sizeof(struct) > len(data)
// (*btrfs_root_ref)(unsafe.Pointer(&p[0])) and sizeof(btrfs_root_ref) == 24
ref := rootRef{
DirID: asUint64(p[0:]),
Sequence: asUint64(p[8:]),
}
if n := asUint16(p[16:]); n > 0 {
ref.Name = string(p[sz : sz+n : sz+n])
}
return ref
}

745
btrfs_tree_h.go Normal file
View File

@ -0,0 +1,745 @@
package btrfs
/*
* This header contains the structure definitions and constants used
* by file system objects that can be retrieved using
* the _BTRFS_IOC_SEARCH_TREE ioctl. That means basically anything that
* is needed to describe a leaf node's key or item contents.
*/
/* holds pointers to all of the tree roots */
/* stores information about which extents are in use, and reference counts */
/*
* chunk tree stores translations from logical -> physical block numbering
* the super block points to the chunk tree
*/
/*
* stores information about which areas of a given device are in use.
* one per device. The tree of tree roots points to the device tree
*/
/* one per subvolume, storing files and directories */
/* directory objectid inside the root tree */
/* holds checksums of all the data extents */
/* holds quota configuration and tracking */
/* for storing items that use the _BTRFS_UUID_KEY* types */
/* tracks free space in block groups. */
/* device stats in the device tree */
/* for storing balance parameters in the root tree */
/* orhpan objectid for tracking unlinked/truncated files */
/* does write ahead logging to speed up fsyncs */
/* for space balancing */
/*
* extent checksums all have this objectid
* this allows them to share the logging tree
* for fsyncs
*/
/* For storing free space cache */
/*
* The inode number assigned to the special inode for storing
* free ino cache
*/
/* dummy objectid represents multiple objectids */
/*
* All files have objectids in this range.
*/
/*
* the device items go into the chunk tree. The key is in the form
* [ 1 _BTRFS_DEV_ITEM_KEY device_id ]
*/
/*
* inode items have the data typically returned from stat and store other
* info about object characteristics. There is one for every file and dir in
* the FS
*/
/* reserve 2-15 close to the inode for later flexibility */
/*
* dir items are the name -> inode pointers in a directory. There is one
* for every name in a directory.
*/
/*
* extent data is for file data
*/
/*
* extent csums are stored in a separate tree and hold csums for
* an entire extent on disk.
*/
/*
* root items point to tree roots. They are typically in the root
* tree used by the super block to find all the other trees
*/
/*
* root backrefs tie subvols and snapshots to the directory entries that
* reference them
*/
/*
* root refs make a fast index for listing all of the snapshots and
* subvolumes referenced by a given root. They point directly to the
* directory item in the root that references the subvol
*/
/*
* extent items are in the extent map tree. These record which blocks
* are used, and how many references there are to each block
*/
/*
* The same as the _BTRFS_EXTENT_ITEM_KEY, except it's metadata we already know
* the length, so we save the level in key->offset instead of the length.
*/
/*
* block groups give us hints into the extent allocation trees. Which
* blocks are free etc etc
*/
/*
* Every block group is represented in the free space tree by a free space info
* item, which stores some accounting information. It is keyed on
* (block_group_start, FREE_SPACE_INFO, block_group_length).
*/
/*
* A free space extent tracks an extent of space that is free in a block group.
* It is keyed on (start, FREE_SPACE_EXTENT, length).
*/
/*
* When a block group becomes very fragmented, we convert it to use bitmaps
* instead of extents. A free space bitmap is keyed on
* (start, FREE_SPACE_BITMAP, length); the corresponding item is a bitmap with
* (length / sectorsize) bits.
*/
/*
* Records the overall state of the qgroups.
* There's only one instance of this key present,
* (0, _BTRFS_QGROUP_STATUS_KEY, 0)
*/
/*
* Records the currently used space of the qgroup.
* One key per qgroup, (0, _BTRFS_QGROUP_INFO_KEY, qgroupid).
*/
/*
* Contains the user configured limits for the qgroup.
* One key per qgroup, (0, _BTRFS_QGROUP_LIMIT_KEY, qgroupid).
*/
/*
* Records the child-parent relationship of qgroups. For
* each relation, 2 keys are present:
* (childid, _BTRFS_QGROUP_RELATION_KEY, parentid)
* (parentid, _BTRFS_QGROUP_RELATION_KEY, childid)
*/
/*
* Obsolete name, see _BTRFS_TEMPORARY_ITEM_KEY.
*/
/*
* The key type for tree items that are stored persistently, but do not need to
* exist for extended period of time. The items can exist in any tree.
*
* [subtype, _BTRFS_TEMPORARY_ITEM_KEY, data]
*
* Existing items:
*
* - balance status item
* (_BTRFS_BALANCE_OBJECTID, _BTRFS_TEMPORARY_ITEM_KEY, 0)
*/
/*
* Obsolete name, see _BTRFS_PERSISTENT_ITEM_KEY
*/
/*
* The key type for tree items that are stored persistently and usually exist
* for a long period, eg. filesystem lifetime. The item kinds can be status
* information, stats or preference values. The item can exist in any tree.
*
* [subtype, _BTRFS_PERSISTENT_ITEM_KEY, data]
*
* Existing items:
*
* - device statistics, store IO stats in the device tree, one key for all
* stats
* (_BTRFS_DEV_STATS_OBJECTID, _BTRFS_DEV_STATS_KEY, 0)
*/
/*
* Persistantly stores the device replace state in the device tree.
* The key is built like this: (0, _BTRFS_DEV_REPLACE_KEY, 0).
*/
/*
* Stores items that allow to quickly map UUIDs to something else.
* These items are part of the filesystem UUID tree.
* The key is built like this:
* (UUID_upper_64_bits, _BTRFS_UUID_KEY*, UUID_lower_64_bits).
*/
/* for UUIDs assigned to * received subvols */
/*
* string items are for debugging. They just store a short string of
* data in the FS
*/
/* 32 bytes in various csum fields */
/* csum types */
/*
* flags definitions for directory entry item type
*
* Used by:
* struct btrfs_dir_item.type
*/
/*
* The key defines the order in the tree, and so it also defines (optimal)
* block layout.
*
* objectid corresponds to the inode number.
*
* type tells us things about the object, and is a kind of stream selector.
* so for a given inode, keys with type of 1 might refer to the inode data,
* type of 2 may point to file data in the btree and type == 3 may point to
* extents.
*
* offset is the starting byte offset for this key in the stream.
*
* btrfs_disk_key is in disk byte order. struct btrfs_key is always
* in cpu native order. Otherwise they are identical and their sizes
* should be the same (ie both packed)
*/
type btrfs_disk_key struct {
objectid uint64
type_ uint8
offset uint64
}
type btrfs_key struct {
objectid uint64
type_ uint8
offset uint64
}
type btrfs_dev_item struct {
devid uint64
total_bytes uint64
bytes_used uint64
io_align uint32
io_width uint32
sector_size uint32
type_ uint64
generation uint64
start_offset uint64
dev_group uint32
seek_speed uint8
bandwidth uint8
uuid UUID
fsid FSID
}
type btrfs_stripe struct {
devid uint64
offset uint64
dev_uuid UUID
}
type btrfs_chunk struct {
length uint64
owner uint64
stripe_len uint64
type_ uint64
io_align uint32
io_width uint32
sector_size uint32
num_stripes uint16
sub_stripes uint16
stripe struct {
devid uint64
offset uint64
dev_uuid UUID
}
}
/* additional stripes go here */
type btrfs_free_space_entry struct {
offset uint64
bytes uint64
type_ uint8
}
type btrfs_free_space_header struct {
location struct {
objectid uint64
type_ uint8
offset uint64
}
generation uint64
num_entries uint64
num_bitmaps uint64
}
/* Super block flags */
/* Errors detected */
/*
* items in the extent btree are used to record the objectid of the
* owner of the block and the number of references
*/
type btrfs_extent_item struct {
refs uint64
generation uint64
flags uint64
}
type btrfs_extent_item_v0 struct {
refs uint32
}
/* following flags only apply to tree blocks */
/* use full backrefs for extent pointers in the block */
/*
* this flag is only used internally by scrub and may be changed at any time
* it is only declared here to avoid collisions
*/
type btrfs_tree_block_info struct {
key struct {
objectid uint64
type_ uint8
offset uint64
}
level uint8
}
type btrfs_extent_data_ref struct {
root uint64
objectid uint64
offset uint64
count uint32
}
type btrfs_shared_data_ref struct {
count uint32
}
type btrfs_extent_inline_ref struct {
type_ uint8
offset uint64
}
/* old style backrefs item */
type btrfs_extent_ref_v0 struct {
root uint64
generation uint64
objectid uint64
count uint32
}
/* dev extents record free space on individual devices. The owner
* field points back to the chunk allocation mapping tree that allocated
* the extent. The chunk tree uuid field is a way to double check the owner
*/
type btrfs_dev_extent struct {
chunk_tree uint64
chunk_objectid uint64
chunk_offset uint64
length uint64
chunk_tree_uuid UUID
}
type btrfs_inode_ref struct {
index uint64
name_len uint16
}
/* name goes here */
type btrfs_inode_extref struct {
parent_objectid uint64
index uint64
name_len uint16
//name [0]uint8
}
/* name goes here */
type btrfs_timespec struct {
sec uint64
nsec uint32
}
type btrfs_inode_item struct {
generation uint64
transid uint64
size uint64
nbytes uint64
block_group uint64
nlink uint32
uid uint32
gid uint32
mode uint32
rdev uint64
flags uint64
sequence uint64
reserved [4]uint64
atime struct {
sec uint64
nsec uint32
}
ctime struct {
sec uint64
nsec uint32
}
mtime struct {
sec uint64
nsec uint32
}
otime struct {
sec uint64
nsec uint32
}
}
type btrfs_dir_log_item struct {
end uint64
}
type btrfs_dir_item struct {
location struct {
objectid uint64
type_ uint8
offset uint64
}
transid uint64
data_len uint16
name_len uint16
type_ uint8
}
/*
* Internal in-memory flag that a subvolume has been marked for deletion but
* still visible as a directory
*/
type btrfs_root_item struct {
inode struct {
generation uint64
transid uint64
size uint64
nbytes uint64
block_group uint64
nlink uint32
uid uint32
gid uint32
mode uint32
rdev uint64
flags uint64
sequence uint64
reserved [4]uint64
atime struct {
sec uint64
nsec uint32
}
ctime struct {
sec uint64
nsec uint32
}
mtime struct {
sec uint64
nsec uint32
}
otime struct {
sec uint64
nsec uint32
}
}
generation uint64
root_dirid uint64
bytenr uint64
byte_limit uint64
bytes_used uint64
last_snapshot uint64
flags uint64
refs uint32
drop_progress struct {
objectid uint64
type_ uint8
offset uint64
}
drop_level uint8
level uint8
generation_v2 uint64
uuid UUID
parent_uuid UUID
received_uuid UUID
ctransid uint64
otransid uint64
stransid uint64
rtransid uint64
ctime struct {
sec uint64
nsec uint32
}
otime struct {
sec uint64
nsec uint32
}
stime struct {
sec uint64
nsec uint32
}
rtime struct {
sec uint64
nsec uint32
}
reserved [8]uint64
}
/*
* this is used for both forward and backward root refs
*/
type btrfs_root_ref struct {
dirid uint64
sequence uint64
name_len uint16
}
type btrfs_disk_balance_args struct {
profiles uint64
usage uint64
usage_min uint32
usage_max uint32
devid uint64
pstart uint64
pend uint64
vstart uint64
vend uint64
target uint64
flags uint64
limit uint64
limit_min uint32
limit_max uint32
stripes_min uint32
stripes_max uint32
unused [6]uint64
}
/*
* store balance parameters to disk so that balance can be properly
* resumed after crash or unmount
*/
type btrfs_balance_item struct {
flags uint64
data struct {
profiles uint64
usage uint64
usage_min uint32
usage_max uint32
devid uint64
pstart uint64
pend uint64
vstart uint64
vend uint64
target uint64
flags uint64
limit uint64
limit_min uint32
limit_max uint32
stripes_min uint32
stripes_max uint32
unused [6]uint64
}
meta struct {
profiles uint64
usage uint64
usage_min uint32
usage_max uint32
devid uint64
pstart uint64
pend uint64
vstart uint64
vend uint64
target uint64
flags uint64
limit uint64
limit_min uint32
limit_max uint32
stripes_min uint32
stripes_max uint32
unused [6]uint64
}
sys struct {
profiles uint64
usage uint64
usage_min uint32
usage_max uint32
devid uint64
pstart uint64
pend uint64
vstart uint64
vend uint64
target uint64
flags uint64
limit uint64
limit_min uint32
limit_max uint32
stripes_min uint32
stripes_max uint32
unused [6]uint64
}
unused [4]uint64
}
type btrfs_file_extent_item struct {
generation uint64
ram_bytes uint64
compression uint8
encryption uint8
other_encoding uint16
type_ uint8
disk_bytenr uint64
disk_num_bytes uint64
offset uint64
num_bytes uint64
}
type btrfs_csum_item struct {
csum uint8
}
type btrfs_dev_stats_item struct {
values [_BTRFS_DEV_STAT_VALUES_MAX]uint64
}
type btrfs_dev_replace_item struct {
src_devid uint64
cursor_left uint64
cursor_right uint64
cont_reading_from_srcdev_mode uint64
replace_state uint64
time_started uint64
time_stopped uint64
num_write_errors uint64
num_uncorrectable_read_errors uint64
}
/* different types of block groups (and chunks) */
const (
_BTRFS_RAID_RAID10 = iota
_BTRFS_RAID_RAID1
_BTRFS_RAID_DUP
_BTRFS_RAID_RAID0
_BTRFS_RAID_SINGLE
_BTRFS_RAID_RAID5
_BTRFS_RAID_RAID6
_BTRFS_NR_RAID_TYPES
)
/*
* We need a bit for restriper to be able to tell when chunks of type
* SINGLE are available. This "extended" profile format is used in
* fs_info->avail_*_alloc_bits (in-memory) and balance item fields
* (on-disk). The corresponding on-disk bit in chunk.type is reserved
* to avoid remappings between two formats in future.
*/
/*
* A fake block group type that is used to communicate global block reserve
* size to userspace via the SPACE_INFO ioctl.
*/
func chunk_to_extended(flags uint64) uint64 {
if flags&uint64(_BTRFS_BLOCK_GROUP_PROFILE_MASK) == 0 {
flags |= uint64(availAllocBitSingle)
}
return flags
}
func extended_to_chunk(flags uint64) uint64 {
return flags &^ uint64(availAllocBitSingle)
}
type btrfs_block_group_item struct {
used uint64
chunk_objectid uint64
flags uint64
}
type btrfs_free_space_info struct {
extent_count uint32
flags uint32
}
func btrfs_qgroup_level(qgroupid uint64) uint64 {
return qgroupid >> uint32(qgroupLevelShift)
}
/*
* is subvolume quota turned on?
*/
/*
* RESCAN is set during the initialization phase
*/
/*
* Some qgroup entries are known to be out of date,
* either because the configuration has changed in a way that
* makes a rescan necessary, or because the fs has been mounted
* with a non-qgroup-aware version.
* Turning qouta off and on again makes it inconsistent, too.
*/
type btrfs_qgroup_status_item struct {
version uint64
generation uint64
flags uint64
rescan uint64
}
type btrfs_qgroup_info_item struct {
generation uint64
rfer uint64
rfer_cmpr uint64
excl uint64
excl_cmpr uint64
}
type btrfs_qgroup_limit_item struct {
flags uint64
max_rfer uint64
max_excl uint64
rsv_rfer uint64
rsv_excl uint64
}

View File

@ -44,37 +44,37 @@ const (
devStatsObjectid = 0
// For storing balance parameters in the root tree
balanceObjectid = 0xfffffffffffffffc /* -4 */
balanceObjectid = (1<<64 - 4)
// Orhpan objectid for tracking unlinked/truncated files
orphanObjectid = 0xfffffffffffffffb /* -5 */
orphanObjectid = (1<<64 - 5)
// Does write ahead logging to speed up fsyncs
treeLogObjectid = 0xfffffffffffffffa /* -6 */
treeLogFixupObjectid = 0xfffffffffffffff9 /* -7 */
treeLogObjectid = (1<<64 - 6)
treeLogFixupObjectid = (1<<64 - 7)
// For space balancing
treeRelocObjectid = 0xfffffffffffffff8 /* -8 */
dataRelocTreeObjectid = 0xfffffffffffffff7 /* -9 */
treeRelocObjectid = (1<<64 - 8)
dataRelocTreeObjectid = (1<<64 - 9)
// Extent checksums all have this objectid
// this allows them to share the logging tree
// for fsyncs
extentCsumObjectid = 0xfffffffffffffff6 /* -10 */
extentCsumObjectid = (1<<64 - 10)
// For storing free space cache
freeSpaceObjectid = 0xfffffffffffffff5 /* -11 */
freeSpaceObjectid = (1<<64 - 11)
// The inode number assigned to the special inode for storing
// free ino cache
freeInoObjectid = 0xfffffffffffffff4 /* -12 */
freeInoObjectid = (1<<64 - 12)
// Dummy objectid represents multiple objectids
multipleObjectids = 0xffffffffffffff01 /* -255 */
multipleObjectids = (1<<64 - 255)
// All files have objectids in this range.
firstFreeObjectid = 256
lastFreeObjectid = 0xffffffffffffff00 /* -256 */
lastFreeObjectid = (1<<64 - 256)
firstChunkTreeObjectid = 256
// The device items go into the chunk tree. The key is in the form
@ -135,6 +135,8 @@ const (
extentDataRefKey = 178
extentRefV0Key = 180
sharedBlockRefKey = 182
sharedDataRefKey = 184
@ -221,6 +223,7 @@ const (
csumSize = 32
// Csum types
csumTypeCrc32 = 0
// Flags definitions for directory entry item type
// Used by:
@ -437,7 +440,12 @@ const (
blockGroupData = (1 << 0)
blockGroupSystem = (1 << 1)
blockGroupMetadata = (1 << 2)
blockGroupRaid0 = (1 << 3)
blockGroupRaid1 = (1 << 4)
blockGroupDup = (1 << 5)
blockGroupRaid10 = (1 << 6)
blockGroupRaid5 = (1 << 7)
blockGroupRaid6 = (1 << 8)
// We need a bit for restriper to be able to tell when chunks of type
// SINGLE are available. This "extended" profile format is used in

View File

@ -17,6 +17,7 @@ func init() {
SubvolumeCmd.AddCommand(
SubvolumeCreateCmd,
SubvolumeDeleteCmd,
SubvolumeListCmd,
)
}
@ -62,6 +63,23 @@ operation is safely stored on the media.`,
},
}
var SubvolumeListCmd = &cobra.Command{
Use: "list <mount>",
Short: "List subvolumes",
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) != 1 {
return fmt.Errorf("expected one destination argument")
}
list, err := btrfs.ListSubVolumes(args[0])
if err == nil {
for _, v := range list {
fmt.Printf("%+v\n", v)
}
}
return err
},
}
var SendCmd = &cobra.Command{
Use: "send [-ve] [-p <parent>] [-c <clone-src>] [-f <outfile>] <subvol> [<subvol>...]",
Short: "Send the subvolume(s) to stdout.",

View File

@ -9,7 +9,6 @@ import (
"log"
"os"
"regexp"
"strconv"
"strings"
"unicode"
)
@ -23,7 +22,7 @@ var (
)
var (
reDefineIntConst = regexp.MustCompile(`#define\s+([A-Za-z_]+)\s+(\(?-?\d+(?:U?LL)?(?:\s*<<\s*\d+)?\)?)`)
reDefineIntConst = regexp.MustCompile(`#define\s+([A-Za-z_][A-Za-z\d_]*)\s+(\(?-?\d+(?:U?LL)?(?:\s*<<\s*\d+)?\)?)`)
reNegULL = regexp.MustCompile(`-(\d+)ULL`)
)
@ -126,11 +125,7 @@ func process(w io.Writer, path string) error {
name, val := sub[1], sub[2]
if sub := reNegULL.FindAllStringSubmatch(val, -1); len(sub) > 0 {
for _, s := range sub {
v, err := strconv.ParseInt(s[1], 10, 64)
if err != nil {
panic(err)
}
val = strings.Replace(val, s[0], fmt.Sprintf("0x%x /* -%s */", uint64(-v), s[1]), -1)
val = strings.Replace(val, s[0], fmt.Sprintf("(1<<64 - %s)", s[1]), -1)
}
}
val = strings.Replace(val, "ULL", "", -1)

View File

@ -19,9 +19,31 @@ const (
UUIDSize = 16
)
var zeroUUID UUID
type UUID [UUIDSize]byte
func (id UUID) String() string { return hex.EncodeToString(id[:]) }
func (id UUID) String() string {
if id == zeroUUID {
return "<zero>"
}
buf := make([]byte, UUIDSize*2+4)
i := 0
i += hex.Encode(buf[i:], id[:4])
buf[i] = '-'
i++
i += hex.Encode(buf[i:], id[4:6])
buf[i] = '-'
i++
i += hex.Encode(buf[i:], id[6:8])
buf[i] = '-'
i++
i += hex.Encode(buf[i:], id[8:10])
buf[i] = '-'
i++
i += hex.Encode(buf[i:], id[10:])
return string(buf)
}
type FSID [FSIDSize]byte
@ -121,7 +143,7 @@ type btrfs_ioctl_scrub_args struct {
flags uint64 // in
progress btrfs_scrub_progress // out
// pad to 1k
unused [1024 - 4*8 - unsafe.Sizeof(btrfs_scrub_progress{})]byte
_ [1024 - 4*8 - unsafe.Sizeof(btrfs_scrub_progress{})]byte
}
type contReadingFromSrcdevMode uint64
@ -168,7 +190,7 @@ type btrfs_ioctl_dev_replace_args_u2 struct {
cmd uint64 // in
result uint64 // out
status btrfs_ioctl_dev_replace_status_params // out
unused [unsafe.Sizeof(btrfs_ioctl_dev_replace_start_params{}) - unsafe.Sizeof(btrfs_ioctl_dev_replace_status_params{})]byte
_ [unsafe.Sizeof(btrfs_ioctl_dev_replace_start_params{}) - unsafe.Sizeof(btrfs_ioctl_dev_replace_status_params{})]byte
spare [64]uint64
}
@ -177,7 +199,7 @@ type btrfs_ioctl_dev_info_args struct {
uuid UUID // in/out
bytes_used uint64 // out
total_bytes uint64 // out
unused [379]uint64 // pad to 4k
_ [379]uint64 // pad to 4k
path [devicePathNameMax]byte // out
}
@ -188,7 +210,7 @@ type btrfs_ioctl_fs_info_args struct {
nodesize uint32 // out
sectorsize uint32 // out
clone_alignment uint32 // out
reserved [122*8 + 4]byte // pad to 1k
_ [122*8 + 4]byte // pad to 1k
}
type btrfs_ioctl_feature_flags struct {
@ -231,7 +253,7 @@ type btrfs_balance_args struct {
limit argRange
stripes_min uint32
stripes_max uint32
unused [48]byte
_ [48]byte
}
// report balance progress to userspace
@ -250,13 +272,13 @@ const (
)
type btrfs_ioctl_balance_args struct {
flags uint64 // in/out
state balanceState // out
data btrfs_balance_args // in/out
meta btrfs_balance_args // in/out
sys btrfs_balance_args // in/out
stat btrfs_balance_progress // out
unused [72 * 8]byte // pad to 1k
flags uint64 // in/out
state balanceState // out
data btrfs_balance_args // in/out
meta btrfs_balance_args // in/out
sys btrfs_balance_args // in/out
stat btrfs_balance_progress // out
_ [72 * 8]byte // pad to 1k
}
const _BTRFS_INO_LOOKUP_PATH_MAX = 4080
@ -283,7 +305,7 @@ type btrfs_ioctl_search_key struct {
max_type uint32
// how many items did userland ask for, and how many are we returning
nr_items uint32
unused [36]byte
_ [36]byte
}
type btrfs_ioctl_search_header struct {
@ -347,7 +369,7 @@ type btrfs_ioctl_same_args struct {
logical_offset uint64 // in - start of extent in source
length uint64 // in - length of extent
dest_count uint16 // in - total elements in info array
reserved [6]byte
_ [6]byte
//info [0]btrfs_ioctl_same_extent_info
}
@ -364,7 +386,7 @@ type btrfs_ioctl_defrag_range_args struct {
// which compression method to use if turning on compression
// for this defrag operation. If unspecified, zlib will be used
compress_type uint32
unused [16]byte // spare for later
_ [16]byte // spare for later
}
type btrfs_ioctl_space_info struct {
@ -388,17 +410,17 @@ type btrfs_data_container struct {
}
type btrfs_ioctl_ino_path_args struct {
inum uint64 // in
size uint64 // in
reserved [32]byte
inum uint64 // in
size uint64 // in
_ [32]byte
// struct btrfs_data_container *fspath; out
fspath uint64 // out
}
type btrfs_ioctl_logical_ino_args struct {
logical uint64 // in
size uint64 // in
reserved [32]byte
logical uint64 // in
size uint64 // in
_ [32]byte
// struct btrfs_data_container *inodes; out
inodes uint64
}
@ -428,7 +450,7 @@ type btrfs_ioctl_get_dev_stats struct {
nr_items uint64 // in/out
flags uint64 // in/out
values [_BTRFS_DEV_STAT_VALUES_MAX]uint64 // out values
unused [128 - 2 - _BTRFS_DEV_STAT_VALUES_MAX]uint64 // pad to 1k
_ [128 - 2 - _BTRFS_DEV_STAT_VALUES_MAX]uint64 // pad to 1k
}
const (
@ -445,7 +467,7 @@ type btrfs_ioctl_quota_ctl_args struct {
type btrfs_ioctl_quota_rescan_args struct {
flags uint64
progress uint64
reserved [6]uint64
_ [6]uint64
}
type btrfs_ioctl_qgroup_assign_args struct {
@ -471,7 +493,7 @@ type btrfs_ioctl_received_subvol_args struct {
stime btrfs_ioctl_timespec // in
rtime btrfs_ioctl_timespec // out
flags uint64 // in
reserved [16]uint64 // in
_ [16]uint64 // in
}
const (
@ -498,7 +520,7 @@ type btrfs_ioctl_send_args struct {
clone_sources *uint64 // in
parent_root uint64 // in
flags uint64 // in
reserved [4]uint64 // in
_ [4]uint64 // in
}
var (

View File

@ -47,6 +47,10 @@ var caseSizes = []struct {
{obj: btrfs_ioctl_timespec{}, size: 16},
{obj: btrfs_ioctl_received_subvol_args{}, size: 200},
{obj: btrfs_ioctl_send_args{}, size: 72},
//{obj:btrfs_root_ref{},size:18},
//{obj:btrfs_root_item{},size:439},
//{obj:btrfs_inode_item{},size:160},
}
func TestSizes(t *testing.T) {

View File

@ -6,6 +6,7 @@ import (
"path/filepath"
"strings"
"syscall"
"time"
)
func checkSubVolumeName(name string) bool {
@ -119,11 +120,13 @@ func SnapshotSubVolume(subvol, dst string, ro bool) error {
if err != nil {
return err
}
defer fdst.Close()
// TODO: make SnapshotSubVolume a method on FS to use existing fd
f, err := openDir(subvol)
if err != nil {
return err
}
defer f.Close()
args := btrfs_ioctl_vol_args_v2{
fd: int64(f.Fd()),
}
@ -139,3 +142,122 @@ func SnapshotSubVolume(subvol, dst string, ro bool) error {
copy(args.name[:], newName)
return iocSnapCreateV2(fdst, &args)
}
func ListSubVolumes(path string) ([]Subvolume, error) {
f, err := openDir(path)
if err != nil {
return nil, err
}
defer f.Close()
//root, err := getPathRootID(f)
//if err != nil {
// return nil, fmt.Errorf("can't get rootid for '%s': %v", path, err)
//}
m, err := listSubVolumes(f)
if err != nil {
return nil, err
}
out := make([]Subvolume, 0, len(m))
for _, v := range m {
out = append(out, v)
}
return out, nil
}
type Subvolume struct {
ObjectID uint64
TransID uint64
Name string
RefTree uint64
DirID uint64
Gen uint64
OGen uint64
Flags uint64
UUID UUID
ParentUUID UUID
ReceivedUUID UUID
OTime time.Time
CTime time.Time
}
func listSubVolumes(f *os.File) (map[uint64]Subvolume, error) {
sk := btrfs_ioctl_search_key{
// search in the tree of tree roots
tree_id: 1,
// Set the min and max to backref keys. The search will
// only send back this type of key now.
min_type: rootBackrefKey,
max_type: rootBackrefKey,
min_objectid: firstFreeObjectid,
// Set all the other params to the max, we'll take any objectid
// and any trans.
max_objectid: lastFreeObjectid,
max_offset: maxUint64,
max_transid: maxUint64,
nr_items: 4096, // just a big number, doesn't matter much
}
m := make(map[uint64]Subvolume)
for {
out, err := treeSearchRaw(f, sk)
if err != nil {
return nil, err
} else if len(out) == 0 {
break
}
for _, obj := range out {
switch obj.Type {
case rootBackrefKey:
ref := asRootRef(obj.Data)
o := m[obj.ObjectID]
o.TransID = obj.TransID
o.ObjectID = obj.ObjectID
o.RefTree = obj.Offset
o.DirID = ref.DirID
o.Name = ref.Name
m[obj.ObjectID] = o
case rootItemKey:
o := m[obj.ObjectID]
o.TransID = obj.TransID
o.ObjectID = obj.ObjectID
// TODO: decode whole object?
o.Gen = asUint64(obj.Data[160:]) // size of btrfs_inode_item
o.Flags = asUint64(obj.Data[160+6*8:])
const sz = 439
const toff = sz - 8*8 - 4*12
o.CTime = asTime(obj.Data[toff+0*12:])
o.OTime = asTime(obj.Data[toff+1*12:])
o.OGen = asUint64(obj.Data[toff-3*8:])
const uoff = toff - 4*8 - 3*UUIDSize
copy(o.UUID[:], obj.Data[uoff+0*UUIDSize:])
copy(o.ParentUUID[:], obj.Data[uoff+1*UUIDSize:])
copy(o.ReceivedUUID[:], obj.Data[uoff+2*UUIDSize:])
m[obj.ObjectID] = o
}
}
// record the mins in key so we can make sure the
// next search doesn't repeat this root
last := out[len(out)-1]
sk.min_objectid = last.ObjectID
sk.min_type = last.Type
sk.min_offset = last.Offset + 1
if sk.min_offset == 0 { // overflow
sk.min_type++
} else {
continue
}
if sk.min_type > rootBackrefKey {
sk.min_type = rootItemKey
sk.min_objectid++
} else {
continue
}
if sk.min_objectid > sk.max_objectid {
break
}
}
return m, nil
}

View File

@ -4,6 +4,7 @@ import (
"fmt"
"os"
"syscall"
"unsafe"
)
func isBtrfs(path string) (bool, error) {
@ -35,3 +36,35 @@ func openDir(path string) (*os.File, error) {
}
return file, nil
}
type rawItem struct {
TransID uint64
ObjectID uint64
Type uint32
Offset uint64
Data []byte
}
func treeSearchRaw(f *os.File, key btrfs_ioctl_search_key) (out []rawItem, _ error) {
args := btrfs_ioctl_search_args{
key: key,
}
if err := iocTreeSearch(f, &args); err != nil {
return nil, err
}
out = make([]rawItem, 0, args.key.nr_items)
buf := args.buf[:]
for i := 0; i < int(args.key.nr_items); i++ {
h := (*btrfs_ioctl_search_header)(unsafe.Pointer(&buf[0]))
buf = buf[unsafe.Sizeof(btrfs_ioctl_search_header{}):]
out = append(out, rawItem{
TransID: h.transid,
ObjectID: h.objectid,
Type: h.typ,
Offset: h.offset,
Data: buf[:h.len], // TODO: reallocate?
})
buf = buf[h.len:]
}
return out, nil
}