btrfs_cli/subvolume/
show.rs1use crate::{Format, Runnable, util::ParsedUuid};
2use anyhow::{Context, Result};
3use btrfs_uapi::{
4 send_receive::subvolume_search_by_uuid,
5 subvolume::{subvolume_info, subvolume_info_by_id},
6};
7use clap::Parser;
8use std::{
9 fs::File,
10 mem,
11 os::unix::io::AsFd,
12 path::PathBuf,
13 time::{SystemTime, UNIX_EPOCH},
14};
15
16#[derive(Parser, Debug)]
24pub struct SubvolumeShowCommand {
25 #[clap(short = 'r', long = "rootid")]
27 pub rootid: Option<u64>,
28
29 #[clap(short = 'u', long = "uuid", conflicts_with = "rootid")]
31 pub uuid: Option<ParsedUuid>,
32
33 pub path: PathBuf,
35}
36
37impl Runnable for SubvolumeShowCommand {
38 fn run(&self, _format: Format, _dry_run: bool) -> Result<()> {
39 let file = File::open(&self.path).with_context(|| {
40 format!("failed to open '{}'", self.path.display())
41 })?;
42
43 let info = if let Some(rootid) = self.rootid {
44 subvolume_info_by_id(file.as_fd(), rootid).with_context(|| {
45 format!("failed to get subvolume info for rootid {rootid}")
46 })?
47 } else if let Some(ref uuid) = self.uuid {
48 let inner = &**uuid;
49 let rootid = subvolume_search_by_uuid(file.as_fd(), inner)
50 .with_context(|| {
51 format!("failed to find subvolume with UUID {inner}")
52 })?;
53 subvolume_info_by_id(file.as_fd(), rootid).with_context(|| {
54 format!("failed to get subvolume info for UUID {inner}")
55 })?
56 } else {
57 subvolume_info(file.as_fd()).with_context(|| {
58 format!(
59 "failed to get subvolume info for '{}'",
60 self.path.display()
61 )
62 })?
63 };
64
65 println!("{}", self.path.display());
66 println!("\tName: \t\t\t{}", info.name);
67 println!("\tUUID: \t\t\t{}", format_uuid(&info.uuid));
68 println!("\tParent UUID: \t\t{}", format_uuid(&info.parent_uuid));
69 println!("\tReceived UUID: \t\t{}", format_uuid(&info.received_uuid));
70 println!("\tCreation time: \t\t{}", format_time(info.otime));
71 println!("\tSubvolume ID: \t\t{}", info.id);
72 println!("\tGeneration: \t\t{}", info.generation);
73 println!("\tGen at creation: \t{}", info.otransid);
74 println!("\tParent ID: \t\t{}", info.parent_id);
75 println!("\tTop level ID: \t\t{}", info.parent_id);
76 println!("\tFlags: \t\t\t{}", info.flags);
77 println!("\tSend transid: \t\t{}", info.stransid);
78 println!("\tSend time: \t\t{}", format_time(info.stime));
79 println!("\tReceive transid: \t{}", info.rtransid);
80 println!("\tReceive time: \t\t{}", format_time(info.rtime));
81
82 Ok(())
83 }
84}
85
86fn format_uuid(uuid: &uuid::Uuid) -> String {
87 if uuid.is_nil() {
88 "-".to_string()
89 } else {
90 uuid.hyphenated().to_string()
91 }
92}
93
94fn format_time(t: SystemTime) -> String {
99 if t == UNIX_EPOCH {
100 return "-".to_string();
101 }
102
103 let secs = match t.duration_since(UNIX_EPOCH) {
104 Ok(d) => d.as_secs() as nix::libc::time_t,
105 Err(_) => return "-".to_string(),
106 };
107
108 let mut tm: nix::libc::tm = unsafe { mem::zeroed() };
112 let result = unsafe { nix::libc::localtime_r(&secs, &mut tm) };
113 if result.is_null() {
114 return "-".to_string();
115 }
116
117 let year = tm.tm_year + 1900;
118 let mon = tm.tm_mon + 1;
119 let mday = tm.tm_mday;
120 let hour = tm.tm_hour;
121 let min = tm.tm_min;
122 let sec = tm.tm_sec;
123
124 let gmtoff = tm.tm_gmtoff; let off_sign = if gmtoff < 0 { '-' } else { '+' };
127 let off_abs = gmtoff.unsigned_abs();
128 let off_h = off_abs / 3600;
129 let off_m = (off_abs % 3600) / 60;
130
131 format!(
132 "{year:04}-{mon:02}-{mday:02} {hour:02}:{min:02}:{sec:02} {off_sign}{off_h:02}{off_m:02}"
133 )
134}