Skip to content

Commit 2d7b4b2

Browse files
slpmtjhrc
andcommitted
virtio/fs/macos: improve [set|get]_attr_stat
The methods [set|get]_attr_stat are used to store and read the guest ownership and permission values as extended attributes on APFS. So far, they were treated in an all-or-nothing fashion: you either have both ownership and permission data, or nothing. Also, when those values were missing, we defaulted to a very conservative root ownership. There are situations in which we may only have partial information (only ownership or only permission). For instance, this happens on files created from the host when the guest attempts to change ownership or the permission bits. In this change, we extend the format to of the attribute we store on APFS to allow having missing fields (signalled with an "x" instead of a u32). Also, when a field is missing, we use the host's ownership and permission bits instead of defaulting to root. This change is backwards compatible, as older versions of libkrun generate the same format strings for the extended attribute, but without missing fields. Co-authored-by: Matej Hrica <matej@hrica.dev> Signed-off-by: Sergio Lopez <slp@redhat.com>
1 parent 4923562 commit 2d7b4b2

File tree

1 file changed

+82
-61
lines changed

1 file changed

+82
-61
lines changed

src/devices/src/virtio/fs/macos/passthrough.rs

Lines changed: 82 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -92,35 +92,29 @@ fn item_to_value(item: &[u8], radix: u32) -> Option<u32> {
9292
}
9393
}
9494

95-
fn get_xattr_common(buf: &[u8]) -> io::Result<Option<(u32, u32, u32)>> {
95+
fn get_xattr_common(buf: &[u8]) -> io::Result<(Option<u32>, Option<u32>, Option<u32>)> {
9696
let mut items = buf.split(|c| *c == b':');
9797

9898
let uid = match items.next() {
99-
Some(item) => match item_to_value(item, 10) {
100-
Some(item) => item,
101-
None => return Ok(None),
102-
},
103-
None => return Ok(None),
99+
Some(item) => item_to_value(item, 10),
100+
None => None,
104101
};
105102
let gid = match items.next() {
106-
Some(item) => match item_to_value(item, 10) {
107-
Some(item) => item,
108-
None => return Ok(None),
109-
},
110-
None => return Ok(None),
103+
Some(item) => item_to_value(item, 10),
104+
None => None,
111105
};
112106
let mode = match items.next() {
113-
Some(item) => match item_to_value(item, 8) {
114-
Some(item) => item,
115-
None => return Ok(None),
116-
},
117-
None => return Ok(None),
107+
Some(item) => item_to_value(item, 8),
108+
None => None,
118109
};
119110

120-
Ok(Some((uid, gid, mode)))
111+
Ok((uid, gid, mode))
121112
}
122113

123-
fn get_xattr_fstat(fd: RawFd, st: bindings::stat64) -> io::Result<Option<(u32, u32, u32)>> {
114+
fn get_xattr_fstat(
115+
fd: RawFd,
116+
st: bindings::stat64,
117+
) -> io::Result<(Option<u32>, Option<u32>, Option<u32>)> {
124118
let mut buf: Vec<u8> = vec![0; 32];
125119
let options = if (st.st_mode & libc::S_IFMT) == libc::S_IFLNK {
126120
libc::XATTR_NOFOLLOW
@@ -139,15 +133,18 @@ fn get_xattr_fstat(fd: RawFd, st: bindings::stat64) -> io::Result<Option<(u32, u
139133
};
140134
if res < 0 {
141135
debug!("fget_xattr error: {res}");
142-
return Ok(None);
136+
return Ok((None, None, None));
143137
}
144138

145139
buf.resize(res as usize, 0);
146140

147141
get_xattr_common(&buf)
148142
}
149143

150-
fn get_xattr_lstat(path: &CString, st: bindings::stat64) -> io::Result<Option<(u32, u32, u32)>> {
144+
fn get_xattr_lstat(
145+
path: &CString,
146+
st: bindings::stat64,
147+
) -> io::Result<(Option<u32>, Option<u32>, Option<u32>)> {
151148
let mut buf: Vec<u8> = vec![0; 32];
152149
let options = if (st.st_mode & libc::S_IFMT) == libc::S_IFLNK {
153150
libc::XATTR_NOFOLLOW
@@ -166,7 +163,7 @@ fn get_xattr_lstat(path: &CString, st: bindings::stat64) -> io::Result<Option<(u
166163
};
167164
if res < 0 {
168165
debug!("fget_xattr error: {res}");
169-
return Ok(None);
166+
return Ok((None, None, None));
170167
}
171168

172169
buf.resize(res as usize, 0);
@@ -198,32 +195,46 @@ fn set_xattr_stat(
198195
0
199196
};
200197

201-
let (new_owner, new_mode) = if is_valid_owner(owner) && mode.is_some() {
202-
(owner.unwrap(), mode.unwrap())
198+
let buf = if is_valid_owner(owner) && mode.is_some() {
199+
let owner = owner.unwrap();
200+
let mode = mode.unwrap();
201+
format!("{}:{}:0{:o}", owner.0, owner.1, mode)
203202
} else {
204-
let (orig_owner, orig_mode) = if let Some((xuid, xgid, xmode)) = match file {
203+
let (orig_uid, orig_gid, orig_mode) = match file {
205204
InodeHandle::Fd(fd) => get_xattr_fstat(*fd, st)?,
206205
InodeHandle::Path(ref c_path) => get_xattr_lstat(c_path, st)?,
207-
} {
208-
((xuid, xgid), xmode)
209-
} else {
210-
((0, 0), 0o0777)
211206
};
212207

213-
let new_owner = match owner {
208+
let (uid, gid) = match owner {
214209
Some(o) => {
215-
let uid = if o.0 < UID_MAX { o.0 } else { orig_owner.0 };
216-
let gid = if o.1 < UID_MAX { o.1 } else { orig_owner.1 };
210+
let uid = if o.0 < UID_MAX { Some(o.0) } else { orig_uid };
211+
let gid = if o.1 < UID_MAX { Some(o.1) } else { orig_gid };
217212
(uid, gid)
218213
}
219-
None => orig_owner,
214+
None => (orig_uid, orig_gid),
220215
};
221216

222-
(new_owner, mode.unwrap_or(orig_mode))
217+
let mut buf = String::new();
218+
if let Some(uid) = uid {
219+
write!(&mut buf, "{uid}");
220+
} else {
221+
buf.push('x');
222+
}
223+
if let Some(gid) = gid {
224+
write!(&mut buf, ":{gid}:");
225+
} else {
226+
buf.push_str(":x:");
227+
}
228+
if let Some(mode) = mode {
229+
write!(&mut buf, "0{mode:o}");
230+
} else if let Some(orig_mode) = orig_mode {
231+
write!(&mut buf, "0{orig_mode:o}");
232+
} else {
233+
buf.push('x');
234+
}
235+
buf
223236
};
224237

225-
let buf = format!("{}:{}:0{:o}", new_owner.0, new_owner.1, new_mode);
226-
227238
let res = match file {
228239
InodeHandle::Path(path) => unsafe {
229240
libc::setxattr(
@@ -254,6 +265,32 @@ fn set_xattr_stat(
254265
}
255266
}
256267

268+
fn stat_common(
269+
mut st: bindings::stat64,
270+
uid: Option<u32>,
271+
gid: Option<u32>,
272+
mode: Option<u32>,
273+
host: bool,
274+
) -> io::Result<bindings::stat64> {
275+
if !host {
276+
if let Some(uid) = uid {
277+
st.st_uid = uid;
278+
}
279+
if let Some(gid) = gid {
280+
st.st_gid = gid;
281+
}
282+
if let Some(mode) = mode {
283+
if mode as u16 & libc::S_IFMT == 0 {
284+
st.st_mode = (st.st_mode & libc::S_IFMT) | mode as u16;
285+
} else {
286+
st.st_mode = mode as u16;
287+
}
288+
}
289+
}
290+
291+
Ok(st)
292+
}
293+
257294
fn fstat(fd: RawFd, host: bool) -> io::Result<bindings::stat64> {
258295
let mut st = MaybeUninit::<bindings::stat64>::zeroed();
259296

@@ -262,21 +299,13 @@ fn fstat(fd: RawFd, host: bool) -> io::Result<bindings::stat64> {
262299
let res = unsafe { libc::fstat(fd, st.as_mut_ptr()) };
263300
if res >= 0 {
264301
// Safe because the kernel guarantees that the struct is now fully initialized.
265-
let mut st = unsafe { st.assume_init() };
266-
302+
let st = unsafe { st.assume_init() };
267303
if !host {
268-
if let Some((uid, gid, mode)) = get_xattr_fstat(fd, st)? {
269-
st.st_uid = uid;
270-
st.st_gid = gid;
271-
if mode as u16 & libc::S_IFMT == 0 {
272-
st.st_mode = (st.st_mode & libc::S_IFMT) | mode as u16;
273-
} else {
274-
st.st_mode = mode as u16;
275-
}
276-
}
304+
let (uid, gid, mode) = get_xattr_fstat(fd, st)?;
305+
stat_common(st, uid, gid, mode, host)
306+
} else {
307+
Ok(st)
277308
}
278-
279-
Ok(st)
280309
} else {
281310
Err(linux_error(io::Error::last_os_error()))
282311
}
@@ -290,21 +319,13 @@ fn lstat(c_path: &CString, host: bool) -> io::Result<bindings::stat64> {
290319
let res = unsafe { libc::lstat(c_path.as_ptr(), st.as_mut_ptr()) };
291320
if res >= 0 {
292321
// Safe because the kernel guarantees that the struct is now fully initialized.
293-
let mut st = unsafe { st.assume_init() };
294-
322+
let st = unsafe { st.assume_init() };
295323
if !host {
296-
if let Some((uid, gid, mode)) = get_xattr_lstat(c_path, st)? {
297-
st.st_uid = uid;
298-
st.st_gid = gid;
299-
if mode as u16 & libc::S_IFMT == 0 {
300-
st.st_mode = (st.st_mode & libc::S_IFMT) | mode as u16;
301-
} else {
302-
st.st_mode = mode as u16;
303-
}
304-
}
324+
let (uid, gid, mode) = get_xattr_lstat(c_path, st)?;
325+
stat_common(st, uid, gid, mode, host)
326+
} else {
327+
Ok(st)
305328
}
306-
307-
Ok(st)
308329
} else {
309330
Err(linux_error(io::Error::last_os_error()))
310331
}

0 commit comments

Comments
 (0)