btrfs_cli/property/
get.rs1use super::{PropertyObjectType, detect_object_types, property_names};
2use crate::{Format, Runnable, util::open_path};
3use anyhow::{Context, Result, anyhow, bail};
4use btrfs_uapi::{filesystem::label_get, subvolume::subvolume_flags_get};
5use clap::Parser;
6use std::{
7 fs::File,
8 os::unix::io::AsFd,
9 path::{Path, PathBuf},
10};
11
12#[derive(Parser, Debug)]
16pub struct PropertyGetCommand {
17 #[clap(short = 't', long = "type")]
19 pub object_type: Option<PropertyObjectType>,
20
21 pub object: PathBuf,
23
24 pub name: Option<String>,
26}
27
28impl Runnable for PropertyGetCommand {
29 fn run(&self, _format: Format, _dry_run: bool) -> Result<()> {
30 let file = open_path(&self.object)?;
31
32 let detected_types = detect_object_types(&self.object);
34 let target_type = match self.object_type {
35 Some(t) => t,
36 None => {
37 if detected_types.len() > 1 {
39 bail!(
40 "object type is ambiguous, please use option -t (detected: {:?})",
41 detected_types
42 );
43 }
44 detected_types
45 .first()
46 .copied()
47 .ok_or_else(|| anyhow!("object is not a btrfs object"))?
48 }
49 };
50
51 if let Some(name) = &self.name {
53 get_property(&file, target_type, name, &self.object)?;
54 } else {
55 for name in property_names(target_type) {
57 let _ = get_property(&file, target_type, name, &self.object);
59 }
60 }
61
62 Ok(())
63 }
64}
65
66fn get_property(
67 file: &File,
68 obj_type: PropertyObjectType,
69 name: &str,
70 path: &Path,
71) -> Result<()> {
72 match (obj_type, name) {
73 (PropertyObjectType::Subvol, "ro") => {
74 let flags =
75 subvolume_flags_get(file.as_fd()).with_context(|| {
76 format!(
77 "failed to get read-only flag for '{}'",
78 path.display()
79 )
80 })?;
81 let is_readonly =
82 flags.contains(btrfs_uapi::subvolume::SubvolumeFlags::RDONLY);
83 println!("ro={}", if is_readonly { "true" } else { "false" });
84 }
85 (PropertyObjectType::Filesystem, "label")
86 | (PropertyObjectType::Device, "label") => {
87 let label = label_get(file.as_fd()).with_context(|| {
88 format!("failed to get label for '{}'", path.display())
89 })?;
90 println!("label={}", label.to_bytes().escape_ascii());
91 }
92 (PropertyObjectType::Inode, "compression") => {
93 get_compression_property(file, path)?;
94 }
95 _ => {
96 bail!(
97 "property '{}' is not applicable to object type {:?}",
98 name,
99 obj_type
100 );
101 }
102 }
103
104 Ok(())
105}
106
107fn get_compression_property(file: &File, path: &Path) -> Result<()> {
108 use nix::libc::{ENODATA, fgetxattr};
109 use std::os::unix::io::AsRawFd;
110
111 let fd = file.as_raw_fd();
112 let xattr_name = "btrfs.compression\0";
113
114 let result = unsafe {
116 fgetxattr(
117 fd,
118 xattr_name.as_ptr() as *const i8,
119 std::ptr::null_mut(),
120 0,
121 )
122 };
123
124 if result < 0 {
125 let errno = nix::errno::Errno::last_raw();
126 if errno == ENODATA {
127 return Ok(());
129 } else {
130 return Err(anyhow::anyhow!(
131 "failed to get compression for '{}': {}",
132 path.display(),
133 nix::errno::Errno::from_raw(errno)
134 ));
135 }
136 }
137
138 let len = result as usize;
139 let mut buf = vec![0u8; len];
140
141 let result = unsafe {
143 fgetxattr(
144 fd,
145 xattr_name.as_ptr() as *const i8,
146 buf.as_mut_ptr() as *mut std::ffi::c_void,
147 len,
148 )
149 };
150
151 if result < 0 {
152 return Err(anyhow::anyhow!(
153 "failed to get compression for '{}'",
154 path.display()
155 ));
156 }
157
158 let value = String::from_utf8_lossy(&buf);
159 println!("compression={}", value);
160
161 Ok(())
162}