devices: fs: Use l{get,set,list,remove}xattr

Using the `open_inode` method on an fd for a symlink results in the
kernel returning -ELOOP.  Since there are no `*at` methods for extended
attributes, manually read the path for the file and then use the
l{get,set,list,remove}xattr method on the returned path.

BUG=b:136128512
TEST=boot arcvm with virtio-fs and selinux enabled

Change-Id: I2fde57db8a075838a3a877309f6cf89059f19258
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/2120763
Auto-Submit: Chirantan Ekbote <chirantan@chromium.org>
Tested-by: kokoro <noreply+kokoro@google.com>
Commit-Queue: Stephen Barber <smbarber@chromium.org>
Reviewed-by: Stephen Barber <smbarber@chromium.org>
This commit is contained in:
Chirantan Ekbote 2020-03-26 16:20:40 +09:00 committed by Commit Bot
parent 9e8aa131d3
commit d74bb77a3e
4 changed files with 55 additions and 25 deletions

View file

@ -356,6 +356,38 @@ impl PassthroughFs {
vec![self.proc.as_raw_fd()]
}
fn get_path(&self, inode: Inode) -> io::Result<CString> {
let data = self
.inodes
.read()
.unwrap()
.get(&inode)
.map(Arc::clone)
.ok_or_else(ebadf)?;
let mut buf = Vec::with_capacity(libc::PATH_MAX as usize);
buf.resize(libc::PATH_MAX as usize, 0);
let path = CString::new(format!("self/fd/{}", data.file.as_raw_fd()))
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
// Safe because this will only modify the contents of `buf` and we check the return value.
let res = unsafe {
libc::readlinkat(
self.proc.as_raw_fd(),
path.as_ptr(),
buf.as_mut_ptr() as *mut libc::c_char,
buf.len(),
)
};
if res < 0 {
return Err(io::Error::last_os_error());
}
buf.resize(res as usize, 0);
CString::new(buf).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
}
fn open_inode(&self, inode: Inode, mut flags: i32) -> io::Result<File> {
let data = self
.inodes
@ -1521,14 +1553,12 @@ impl FileSystem for PassthroughFs {
value: &[u8],
flags: u32,
) -> io::Result<()> {
// The f{set,get,remove,list}xattr functions don't work on an fd opened with `O_PATH` so we
// need to get a new fd.
let file = self.open_inode(inode, libc::O_RDONLY | libc::O_NONBLOCK)?;
let path = self.get_path(inode)?;
// Safe because this doesn't modify any memory and we check the return value.
let res = unsafe {
libc::fsetxattr(
file.as_raw_fd(),
libc::lsetxattr(
path.as_ptr(),
name.as_ptr(),
value.as_ptr() as *const libc::c_void,
value.len(),
@ -1549,17 +1579,15 @@ impl FileSystem for PassthroughFs {
name: &CStr,
size: u32,
) -> io::Result<GetxattrReply> {
// The f{set,get,remove,list}xattr functions don't work on an fd opened with `O_PATH` so we
// need to get a new fd.
let file = self.open_inode(inode, libc::O_RDONLY | libc::O_NONBLOCK)?;
let path = self.get_path(inode)?;
let mut buf = Vec::with_capacity(size as usize);
buf.resize(size as usize, 0);
// Safe because this will only modify the contents of `buf`.
let res = unsafe {
libc::fgetxattr(
file.as_raw_fd(),
libc::lgetxattr(
path.as_ptr(),
name.as_ptr(),
buf.as_mut_ptr() as *mut libc::c_void,
size as libc::size_t,
@ -1578,17 +1606,15 @@ impl FileSystem for PassthroughFs {
}
fn listxattr(&self, _ctx: Context, inode: Inode, size: u32) -> io::Result<ListxattrReply> {
// The f{set,get,remove,list}xattr functions don't work on an fd opened with `O_PATH` so we
// need to get a new fd.
let file = self.open_inode(inode, libc::O_RDONLY | libc::O_NONBLOCK)?;
let path = self.get_path(inode)?;
let mut buf = Vec::with_capacity(size as usize);
buf.resize(size as usize, 0);
// Safe because this will only modify the contents of `buf`.
let res = unsafe {
libc::flistxattr(
file.as_raw_fd(),
libc::llistxattr(
path.as_ptr(),
buf.as_mut_ptr() as *mut libc::c_char,
size as libc::size_t,
)
@ -1606,12 +1632,10 @@ impl FileSystem for PassthroughFs {
}
fn removexattr(&self, _ctx: Context, inode: Inode, name: &CStr) -> io::Result<()> {
// The f{set,get,remove,list}xattr functions don't work on an fd opened with `O_PATH` so we
// need to get a new fd.
let file = self.open_inode(inode, libc::O_RDONLY | libc::O_NONBLOCK)?;
let path = self.get_path(inode)?;
// Safe because this doesn't modify any memory and we check the return value.
let res = unsafe { libc::fremovexattr(file.as_raw_fd(), name.as_ptr()) };
let res = unsafe { libc::lremovexattr(path.as_ptr(), name.as_ptr()) };
if res == 0 {
Ok(())

View file

@ -9,8 +9,10 @@ fallocate: 1
fchmodat: 1
fchownat: 1
fdatasync: 1
fgetxattr: 1
fsetxattr: 1
lgetxattr: 1
lsetxattr: 1
llistxattr: 1
lremovexattr: 1
fsync: 1
newfstatat: 1
fstatfs: 1

View file

@ -9,8 +9,10 @@ fallocate: 1
fchmodat: 1
fchownat: 1
fdatasync: 1
fgetxattr: 1
fsetxattr: 1
lgetxattr: 1
lsetxattr: 1
llistxattr: 1
lremovexattr: 1
fstatat64: 1
fstatfs64: 1
fsync: 1

View file

@ -9,8 +9,10 @@ fallocate: 1
fchmodat: 1
fchownat: 1
fdatasync: 1
fgetxattr: 1
fsetxattr: 1
lgetxattr: 1
lsetxattr: 1
llistxattr: 1
lremovexattr: 1
fstatfs: 1
fsync: 1
ftruncate: 1