Positioned IO.
This commit is contained in:
Generated
+2
-2
@@ -1100,7 +1100,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "redox-scheme"
|
||||
version = "0.2.0"
|
||||
source = "git+https://gitlab.redox-os.org/4lDO2/redox-scheme.git?branch=schemev2#aeff1a7ef5d456679437551a910e5b22a213d8a7"
|
||||
source = "git+https://gitlab.redox-os.org/4lDO2/redox-scheme.git?branch=schemev2plus#ca239589c873374a17715b637be1f9385345721c"
|
||||
dependencies = [
|
||||
"libredox 0.1.3",
|
||||
"redox_syscall 0.5.1",
|
||||
@@ -1137,7 +1137,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.1"
|
||||
source = "git+https://gitlab.redox-os.org/4lDO2/syscall.git?branch=schemev2#ef3273b17114fe02848eaccf102119fc7fdd6056"
|
||||
source = "git+https://gitlab.redox-os.org/4lDO2/syscall.git?branch=schemev2plus#b4f9a8952cec870ed7f457e3c15652d09aa0c9c7"
|
||||
dependencies = [
|
||||
"bitflags 2.5.0",
|
||||
]
|
||||
|
||||
+1
-1
@@ -47,4 +47,4 @@ lto = "fat"
|
||||
mio = { git = "https://gitlab.redox-os.org/redox-os/mio.git", branch = "redox-unix" }
|
||||
orbclient = { git = "https://gitlab.redox-os.org/redox-os/orbclient.git", version = "0.3.44" }
|
||||
redox-daemon = { git = "https://gitlab.redox-os.org/redox-os/redox-daemon.git" }
|
||||
redox_syscall = { git = "https://gitlab.redox-os.org/4lDO2/syscall.git", branch = "schemev2", features = ["std"] }
|
||||
redox_syscall = { git = "https://gitlab.redox-os.org/4lDO2/syscall.git", branch = "schemev2plus", features = ["std"] }
|
||||
|
||||
@@ -15,5 +15,5 @@ common = { path = "../../common" }
|
||||
driver-block = { path = "../driver-block" }
|
||||
pcid = { path = "../../pcid" }
|
||||
libredox = "0.1.3"
|
||||
redox-scheme = { git = "https://gitlab.redox-os.org/4lDO2/redox-scheme.git", branch = "schemev2" }
|
||||
redox-scheme = { git = "https://gitlab.redox-os.org/4lDO2/redox-scheme.git", branch = "schemev2plus" }
|
||||
redox_event = "0.4"
|
||||
|
||||
+44
-88
@@ -1,12 +1,11 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::{cmp, str};
|
||||
use std::str;
|
||||
use std::fmt::Write;
|
||||
use std::io::prelude::*;
|
||||
|
||||
use driver_block::{Disk, DiskWrapper};
|
||||
use syscall::{
|
||||
Error, EACCES, EBADF, EINVAL, EISDIR, ENOENT, ENOLCK, EOVERFLOW, Result, Io, Stat, MODE_DIR,
|
||||
MODE_FILE, O_DIRECTORY, O_STAT, SEEK_CUR, SEEK_END, SEEK_SET,
|
||||
MODE_FILE, O_DIRECTORY, O_STAT,
|
||||
};
|
||||
use redox_scheme::SchemeBlockMut;
|
||||
|
||||
@@ -14,9 +13,9 @@ use crate::ahci::hba::HbaMem;
|
||||
|
||||
#[derive(Clone)]
|
||||
enum Handle {
|
||||
List(Vec<u8>, usize), // Dir contents buffer, position
|
||||
Disk(usize, usize), // Disk index, position
|
||||
Partition(usize, u32, usize), // Disk index, partition index, position
|
||||
List(Vec<u8>), // Dir contents buffer
|
||||
Disk(usize), // Disk index
|
||||
Partition(usize, u32), // Disk index, partition index
|
||||
}
|
||||
|
||||
pub struct DiskScheme {
|
||||
@@ -61,10 +60,10 @@ impl DiskScheme {
|
||||
fn check_locks(&self, disk_i: usize, part_i_opt: Option<u32>) -> Result<()> {
|
||||
for (_, handle) in self.handles.iter() {
|
||||
match handle {
|
||||
Handle::Disk(i, _) => if disk_i == *i {
|
||||
Handle::Disk(i) => if disk_i == *i {
|
||||
return Err(Error::new(ENOLCK));
|
||||
},
|
||||
Handle::Partition(i, p, _) => if disk_i == *i {
|
||||
Handle::Partition(i, p) => if disk_i == *i {
|
||||
match part_i_opt {
|
||||
Some(part_i) => if part_i == *p {
|
||||
return Err(Error::new(ENOLCK));
|
||||
@@ -102,7 +101,7 @@ impl SchemeBlockMut for DiskScheme {
|
||||
|
||||
let id = self.next_id;
|
||||
self.next_id += 1;
|
||||
self.handles.insert(id, Handle::List(list.into_bytes(), 0));
|
||||
self.handles.insert(id, Handle::List(list.into_bytes()));
|
||||
Ok(Some(id))
|
||||
} else {
|
||||
Err(Error::new(EISDIR))
|
||||
@@ -125,7 +124,7 @@ impl SchemeBlockMut for DiskScheme {
|
||||
|
||||
let id = self.next_id;
|
||||
self.next_id += 1;
|
||||
self.handles.insert(id, Handle::Partition(i, p, 0));
|
||||
self.handles.insert(id, Handle::Partition(i, p));
|
||||
Ok(Some(id))
|
||||
} else {
|
||||
Err(Error::new(ENOENT))
|
||||
@@ -138,7 +137,7 @@ impl SchemeBlockMut for DiskScheme {
|
||||
|
||||
let id = self.next_id;
|
||||
self.next_id += 1;
|
||||
self.handles.insert(id, Handle::Disk(i, 0));
|
||||
self.handles.insert(id, Handle::Disk(i));
|
||||
Ok(Some(id))
|
||||
} else {
|
||||
Err(Error::new(ENOENT))
|
||||
@@ -167,19 +166,19 @@ impl SchemeBlockMut for DiskScheme {
|
||||
|
||||
fn fstat(&mut self, id: usize, stat: &mut Stat) -> Result<Option<usize>> {
|
||||
match *self.handles.get(&id).ok_or(Error::new(EBADF))? {
|
||||
Handle::List(ref data, _) => {
|
||||
Handle::List(ref data) => {
|
||||
stat.st_mode = MODE_DIR;
|
||||
stat.st_size = data.len() as u64;
|
||||
Ok(Some(0))
|
||||
},
|
||||
Handle::Disk(number, _) => {
|
||||
Handle::Disk(number) => {
|
||||
let disk = self.disks.get_mut(number).ok_or(Error::new(EBADF))?;
|
||||
stat.st_mode = MODE_FILE;
|
||||
stat.st_size = disk.size();
|
||||
stat.st_blksize = disk.block_length()?;
|
||||
Ok(Some(0))
|
||||
}
|
||||
Handle::Partition(disk_id, part_num, _) => {
|
||||
Handle::Partition(disk_id, part_num) => {
|
||||
let disk = self.disks.get_mut(disk_id).ok_or(Error::new(EBADF))?;
|
||||
let size = {
|
||||
let pt = disk.pt.as_ref().ok_or(Error::new(EBADF))?;
|
||||
@@ -215,8 +214,8 @@ impl SchemeBlockMut for DiskScheme {
|
||||
}
|
||||
|
||||
match *handle {
|
||||
Handle::List(_, _) => (),
|
||||
Handle::Disk(number, _) => {
|
||||
Handle::List(_) => (),
|
||||
Handle::Disk(number) => {
|
||||
let number_str = format!("{}", number);
|
||||
let number_bytes = number_str.as_bytes();
|
||||
j = 0;
|
||||
@@ -226,7 +225,7 @@ impl SchemeBlockMut for DiskScheme {
|
||||
j += 1;
|
||||
}
|
||||
}
|
||||
Handle::Partition(disk_num, part_num, _) => {
|
||||
Handle::Partition(disk_num, part_num) => {
|
||||
let path = format!("{}p{}", disk_num, part_num);
|
||||
let path_bytes = path.as_bytes();
|
||||
j = 0;
|
||||
@@ -241,71 +240,60 @@ impl SchemeBlockMut for DiskScheme {
|
||||
Ok(Some(i))
|
||||
}
|
||||
|
||||
fn read(&mut self, id: usize, buf: &mut [u8]) -> Result<Option<usize>> {
|
||||
fn read(&mut self, id: usize, buf: &mut [u8], offset: u64, _fcntl_flags: u32) -> Result<Option<usize>> {
|
||||
match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
|
||||
Handle::List(ref handle, ref mut size) => {
|
||||
let count = (&handle[*size..]).read(buf).unwrap();
|
||||
*size += count;
|
||||
Ok(Some(count))
|
||||
Handle::List(ref handle) => {
|
||||
let src = usize::try_from(offset).ok().and_then(|o| handle.get(o..)).unwrap_or(&[]);
|
||||
|
||||
let byte_count = core::cmp::min(src.len(), buf.len());
|
||||
buf[..byte_count].copy_from_slice(&src[..byte_count]);
|
||||
Ok(Some(byte_count))
|
||||
},
|
||||
Handle::Disk(number, ref mut size) => {
|
||||
Handle::Disk(number) => {
|
||||
let disk = self.disks.get_mut(number).ok_or(Error::new(EBADF))?;
|
||||
let blk_len = disk.block_length()?;
|
||||
if let Some(count) = disk.read((*size as u64)/(blk_len as u64), buf)? {
|
||||
*size += count;
|
||||
Ok(Some(count))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
disk.read(offset / u64::from(blk_len), buf)
|
||||
}
|
||||
Handle::Partition(disk_num, part_num, ref mut position) => {
|
||||
Handle::Partition(disk_num, part_num) => {
|
||||
let disk = self.disks.get_mut(disk_num).ok_or(Error::new(EBADF))?;
|
||||
let blksize = disk.block_length()?;
|
||||
|
||||
// validate that we're actually reading within the bounds of the partition
|
||||
let rel_block = *position as u64 / blksize as u64;
|
||||
let rel_block = offset / u64::from(blksize);
|
||||
|
||||
let abs_block = {
|
||||
let pt = disk.pt.as_ref().ok_or(Error::new(EBADF))?;
|
||||
let partition = pt.partitions.get(part_num as usize).ok_or(Error::new(EBADF))?;
|
||||
|
||||
let abs_block = partition.start_lba + rel_block;
|
||||
// TODO: This shouldn't return EOVERFLOW?
|
||||
if rel_block >= partition.size {
|
||||
return Err(Error::new(EOVERFLOW));
|
||||
}
|
||||
abs_block
|
||||
};
|
||||
|
||||
if let Some(count) = disk.read(abs_block, buf)? {
|
||||
Ok(Some(count))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
disk.read(abs_block, buf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&mut self, id: usize, buf: &[u8]) -> Result<Option<usize>> {
|
||||
fn write(&mut self, id: usize, buf: &[u8], offset: u64, _fcntl_flags: u32) -> Result<Option<usize>> {
|
||||
match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
|
||||
Handle::List(_, _) => {
|
||||
Handle::List(_) => {
|
||||
Err(Error::new(EBADF))
|
||||
},
|
||||
Handle::Disk(number, ref mut size) => {
|
||||
Handle::Disk(number) => {
|
||||
let disk = self.disks.get_mut(number).ok_or(Error::new(EBADF))?;
|
||||
let blk_len = disk.block_length()?;
|
||||
if let Some(count) = disk.write((*size as u64)/(blk_len as u64), buf)? {
|
||||
*size += count;
|
||||
Ok(Some(count))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
disk.write(offset / u64::from(blk_len), buf)
|
||||
}
|
||||
Handle::Partition(disk_num, part_num, ref mut position) => {
|
||||
Handle::Partition(disk_num, part_num) => {
|
||||
let disk = self.disks.get_mut(disk_num).ok_or(Error::new(EBADF))?;
|
||||
let blksize = disk.block_length()?;
|
||||
|
||||
// validate that we're actually reading within the bounds of the partition
|
||||
let rel_block = *position as u64 / blksize as u64;
|
||||
let rel_block = offset / u64::from(blksize);
|
||||
|
||||
let abs_block = {
|
||||
let pt = disk.pt.as_ref().ok_or(Error::new(EBADF))?;
|
||||
@@ -318,56 +306,24 @@ impl SchemeBlockMut for DiskScheme {
|
||||
abs_block
|
||||
};
|
||||
|
||||
if let Some(count) = disk.write(abs_block, buf)? {
|
||||
Ok(Some(count))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
disk.write(abs_block, buf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn seek(&mut self, id: usize, pos: isize, whence: usize) -> Result<Option<isize>> {
|
||||
let pos = pos as usize;
|
||||
|
||||
match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
|
||||
Handle::List(ref mut handle, ref mut size) => {
|
||||
let len = handle.len() as usize;
|
||||
*size = match whence {
|
||||
SEEK_SET => cmp::min(len, pos),
|
||||
SEEK_CUR => cmp::max(0, cmp::min(len as isize, *size as isize + pos as isize)) as usize,
|
||||
SEEK_END => cmp::max(0, cmp::min(len as isize, len as isize + pos as isize)) as usize,
|
||||
_ => return Err(Error::new(EINVAL))
|
||||
};
|
||||
|
||||
Ok(Some(*size as isize))
|
||||
},
|
||||
Handle::Disk(number, ref mut size) => {
|
||||
fn fsize(&mut self, id: usize) -> Result<Option<u64>> {
|
||||
Ok(Some(match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
|
||||
Handle::List(ref mut handle) => handle.len() as u64,
|
||||
Handle::Disk(number) => {
|
||||
let disk = self.disks.get_mut(number).ok_or(Error::new(EBADF))?;
|
||||
let len = disk.size() as usize;
|
||||
*size = match whence {
|
||||
SEEK_SET => cmp::min(len, pos),
|
||||
SEEK_CUR => cmp::max(0, cmp::min(len as isize, *size as isize + pos as isize)) as usize,
|
||||
SEEK_END => cmp::max(0, cmp::min(len as isize, len as isize + pos as isize)) as usize,
|
||||
_ => return Err(Error::new(EINVAL))
|
||||
};
|
||||
|
||||
Ok(Some(*size as isize))
|
||||
disk.size()
|
||||
}
|
||||
Handle::Partition(disk_num, part_num, ref mut position) => {
|
||||
Handle::Partition(disk_num, part_num) => {
|
||||
let disk = self.disks.get_mut(disk_num).ok_or(Error::new(EBADF))?;
|
||||
let block_count = disk.pt.as_ref().ok_or(Error::new(EBADF))?.partitions.get(part_num as usize).ok_or(Error::new(EBADF))?.size;
|
||||
let len = u64::from(disk.block_length()?) * block_count;
|
||||
|
||||
*position = match whence {
|
||||
SEEK_SET => cmp::min(len as usize, pos) as usize, // Why isn't pos u64?
|
||||
SEEK_CUR => cmp::max(0, cmp::min(len as isize, *position as isize + pos as isize)) as usize,
|
||||
SEEK_END => cmp::max(0, cmp::min(len as isize, len as isize + pos as isize)) as usize,
|
||||
_ => return Err(Error::new(EINVAL)),
|
||||
};
|
||||
Ok(Some(*position as isize))
|
||||
u64::from(disk.block_length()?) * block_count
|
||||
}
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
fn close(&mut self, id: usize) -> Result<Option<usize>> {
|
||||
|
||||
@@ -12,7 +12,7 @@ log = "0.4"
|
||||
redox-daemon = "0.1"
|
||||
redox-log = "0.1"
|
||||
redox_syscall = { version = "0.5", features = ["std"] }
|
||||
redox-scheme = { git = "https://gitlab.redox-os.org/4lDO2/redox-scheme.git", branch = "schemev2" }
|
||||
redox-scheme = { git = "https://gitlab.redox-os.org/4lDO2/redox-scheme.git", branch = "schemev2plus" }
|
||||
redox_event = "0.4"
|
||||
partitionlib = { git = "https://gitlab.redox-os.org/redox-os/partitionlib.git" }
|
||||
smallvec = "1"
|
||||
|
||||
+39
-98
@@ -18,9 +18,9 @@ use partitionlib::{LogicalBlockSize, PartitionTable};
|
||||
|
||||
#[derive(Clone)]
|
||||
enum Handle {
|
||||
List(Vec<u8>, usize), // entries, offset
|
||||
Disk(u32, usize), // disk num, offset
|
||||
Partition(u32, u32, usize), // disk num, part num, offset
|
||||
List(Vec<u8>), // entries
|
||||
Disk(u32), // disk num
|
||||
Partition(u32, u32), // disk num, part num
|
||||
}
|
||||
|
||||
pub struct DiskWrapper {
|
||||
@@ -163,10 +163,10 @@ impl DiskScheme {
|
||||
fn check_locks(&self, disk_i: u32, part_i_opt: Option<u32>) -> Result<()> {
|
||||
for (_, handle) in self.handles.iter() {
|
||||
match handle {
|
||||
Handle::Disk(i, _) => if disk_i == *i {
|
||||
Handle::Disk(i) => if disk_i == *i {
|
||||
return Err(Error::new(ENOLCK));
|
||||
},
|
||||
Handle::Partition(i, p, _) => if disk_i == *i {
|
||||
Handle::Partition(i, p) => if disk_i == *i {
|
||||
match part_i_opt {
|
||||
Some(part_i) => if part_i == *p {
|
||||
return Err(Error::new(ENOLCK));
|
||||
@@ -205,7 +205,7 @@ impl SchemeBlockMut for DiskScheme {
|
||||
|
||||
let id = self.next_id;
|
||||
self.next_id += 1;
|
||||
self.handles.insert(id, Handle::List(list.into_bytes(), 0));
|
||||
self.handles.insert(id, Handle::List(list.into_bytes()));
|
||||
Ok(Some(id))
|
||||
} else {
|
||||
Err(Error::new(EISDIR))
|
||||
@@ -235,7 +235,7 @@ impl SchemeBlockMut for DiskScheme {
|
||||
let id = self.next_id;
|
||||
self.next_id += 1;
|
||||
self.handles
|
||||
.insert(id, Handle::Partition(nsid, part_num, 0));
|
||||
.insert(id, Handle::Partition(nsid, part_num));
|
||||
Ok(Some(id))
|
||||
} else {
|
||||
Err(Error::new(ENOENT))
|
||||
@@ -251,7 +251,7 @@ impl SchemeBlockMut for DiskScheme {
|
||||
|
||||
let id = self.next_id;
|
||||
self.next_id += 1;
|
||||
self.handles.insert(id, Handle::Disk(nsid, 0));
|
||||
self.handles.insert(id, Handle::Disk(nsid));
|
||||
Ok(Some(id))
|
||||
} else {
|
||||
Err(Error::new(ENOENT))
|
||||
@@ -280,12 +280,12 @@ impl SchemeBlockMut for DiskScheme {
|
||||
|
||||
fn fstat(&mut self, id: usize, stat: &mut Stat) -> Result<Option<usize>> {
|
||||
match *self.handles.get(&id).ok_or(Error::new(EBADF))? {
|
||||
Handle::List(ref data, _) => {
|
||||
Handle::List(ref data) => {
|
||||
stat.st_mode = MODE_DIR;
|
||||
stat.st_size = data.len() as u64;
|
||||
Ok(Some(0))
|
||||
}
|
||||
Handle::Disk(number, _) => {
|
||||
Handle::Disk(number) => {
|
||||
let disk = self.disks.get_mut(&number).ok_or(Error::new(EBADF))?;
|
||||
stat.st_mode = MODE_FILE;
|
||||
stat.st_blocks = disk.as_ref().blocks;
|
||||
@@ -297,7 +297,7 @@ impl SchemeBlockMut for DiskScheme {
|
||||
stat.st_size = disk.as_ref().blocks * disk.as_ref().block_size;
|
||||
Ok(Some(0))
|
||||
}
|
||||
Handle::Partition(disk_num, part_num, _) => {
|
||||
Handle::Partition(disk_num, part_num) => {
|
||||
let disk = self.disks.get_mut(&disk_num).ok_or(Error::new(EBADF))?;
|
||||
let part = disk
|
||||
.pt
|
||||
@@ -338,8 +338,8 @@ impl SchemeBlockMut for DiskScheme {
|
||||
}
|
||||
|
||||
match *handle {
|
||||
Handle::List(_, _) => (),
|
||||
Handle::Disk(number, _) => {
|
||||
Handle::List(_) => (),
|
||||
Handle::Disk(number) => {
|
||||
let number_str = format!("{}", number);
|
||||
let number_bytes = number_str.as_bytes();
|
||||
j = 0;
|
||||
@@ -349,7 +349,7 @@ impl SchemeBlockMut for DiskScheme {
|
||||
j += 1;
|
||||
}
|
||||
}
|
||||
Handle::Partition(disk_num, part_num, _) => {
|
||||
Handle::Partition(disk_num, part_num) => {
|
||||
let number_str = format!("{}p{}", disk_num, part_num);
|
||||
let number_bytes = number_str.as_bytes();
|
||||
j = 0;
|
||||
@@ -364,24 +364,20 @@ impl SchemeBlockMut for DiskScheme {
|
||||
Ok(Some(i))
|
||||
}
|
||||
|
||||
fn read(&mut self, id: usize, buf: &mut [u8]) -> Result<Option<usize>> {
|
||||
fn read(&mut self, id: usize, buf: &mut [u8], offset: u64, _fcntl_flags: u32) -> Result<Option<usize>> {
|
||||
match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
|
||||
Handle::List(ref mut handle, ref mut size) => {
|
||||
let count = (&handle[*size..]).read(buf).unwrap();
|
||||
*size += count;
|
||||
Handle::List(ref handle) => {
|
||||
let src = usize::try_from(offset).ok().and_then(|o| handle.get(o..)).unwrap_or(&[]);
|
||||
let count = core::cmp::min(src.len(), buf.len());
|
||||
buf[..count].copy_from_slice(&src[..count]);
|
||||
Ok(Some(count))
|
||||
}
|
||||
Handle::Disk(number, ref mut size) => {
|
||||
Handle::Disk(number) => {
|
||||
let disk = self.disks.get_mut(&number).ok_or(Error::new(EBADF))?;
|
||||
let block_size = disk.as_ref().block_size;
|
||||
if let Some(count) = self.nvme.namespace_read(disk.as_ref(), disk.as_ref().id, (*size as u64) / block_size, buf)? {
|
||||
*size += count;
|
||||
Ok(Some(count))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
self.nvme.namespace_read(disk.as_ref(), disk.as_ref().id, offset / block_size, buf)
|
||||
}
|
||||
Handle::Partition(disk_num, part_num, ref mut offset) => {
|
||||
Handle::Partition(disk_num, part_num) => {
|
||||
let disk = self.disks.get_mut(&disk_num).ok_or(Error::new(EBADF))?;
|
||||
let part = disk
|
||||
.pt
|
||||
@@ -392,38 +388,27 @@ impl SchemeBlockMut for DiskScheme {
|
||||
.ok_or(Error::new(EBADF))?;
|
||||
|
||||
let block_size = disk.as_ref().block_size;
|
||||
let rel_block = (*offset as u64) / block_size;
|
||||
let rel_block = offset / block_size;
|
||||
if rel_block >= part.size {
|
||||
return Err(Error::new(EOVERFLOW));
|
||||
}
|
||||
|
||||
let abs_block = part.start_lba + rel_block;
|
||||
|
||||
if let Some(count) = self.nvme.namespace_read(disk.as_ref(), disk.as_ref().id, abs_block, buf)? {
|
||||
*offset += count;
|
||||
Ok(Some(count))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
self.nvme.namespace_read(disk.as_ref(), disk.as_ref().id, abs_block, buf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&mut self, id: usize, buf: &[u8]) -> Result<Option<usize>> {
|
||||
fn write(&mut self, id: usize, buf: &[u8], offset: u64, _fcntl_flags: u32) -> Result<Option<usize>> {
|
||||
match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
|
||||
Handle::List(_, _) => Err(Error::new(EBADF)),
|
||||
Handle::Disk(number, ref mut size) => {
|
||||
Handle::List(_) => Err(Error::new(EBADF)),
|
||||
Handle::Disk(number) => {
|
||||
let disk = self.disks.get_mut(&number).ok_or(Error::new(EBADF))?;
|
||||
let block_size = disk.as_ref().block_size;
|
||||
if let Some(count) = self.nvme
|
||||
.namespace_write(disk.as_ref(), disk.as_ref().id, (*size as u64) / block_size, buf)? {
|
||||
*size += count;
|
||||
Ok(Some(count))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
self.nvme.namespace_write(disk.as_ref(), disk.as_ref().id, offset / block_size, buf)
|
||||
}
|
||||
Handle::Partition(disk_num, part_num, ref mut offset) => {
|
||||
Handle::Partition(disk_num, part_num) => {
|
||||
let disk = self.disks.get_mut(&disk_num).ok_or(Error::new(EBADF))?;
|
||||
let part = disk
|
||||
.pt
|
||||
@@ -434,57 +419,26 @@ impl SchemeBlockMut for DiskScheme {
|
||||
.ok_or(Error::new(EBADF))?;
|
||||
|
||||
let block_size = disk.as_ref().block_size;
|
||||
let rel_block = (*offset as u64) / block_size;
|
||||
let rel_block = offset / block_size;
|
||||
if rel_block >= part.size {
|
||||
return Err(Error::new(EOVERFLOW));
|
||||
}
|
||||
|
||||
let abs_block = part.start_lba + rel_block;
|
||||
|
||||
if let Some(count) = self.nvme.namespace_write(disk.as_ref(), disk.as_ref().id, abs_block, buf)? {
|
||||
*offset += count;
|
||||
Ok(Some(count))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
self.nvme.namespace_write(disk.as_ref(), disk.as_ref().id, abs_block, buf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn seek(&mut self, id: usize, pos: isize, whence: usize) -> Result<Option<isize>> {
|
||||
match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
|
||||
Handle::List(ref mut handle, ref mut size) => {
|
||||
let len = handle.len() as isize;
|
||||
*size = match whence {
|
||||
SEEK_SET => cmp::min(len, pos),
|
||||
SEEK_CUR => {
|
||||
cmp::max(0, cmp::min(len, *size as isize + pos))
|
||||
}
|
||||
SEEK_END => {
|
||||
cmp::max(0, cmp::min(len, len + pos))
|
||||
}
|
||||
_ => return Err(Error::new(EINVAL)),
|
||||
} as usize;
|
||||
|
||||
Ok(Some(*size as isize))
|
||||
}
|
||||
Handle::Disk(number, ref mut size) => {
|
||||
fn fsize(&mut self, id: usize) -> Result<Option<u64>> {
|
||||
Ok(Some(match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
|
||||
Handle::List(ref handle) => handle.len() as u64,
|
||||
Handle::Disk(number) => {
|
||||
let disk = self.disks.get_mut(&number).ok_or(Error::new(EBADF))?;
|
||||
let len = (disk.as_ref().blocks * disk.as_ref().block_size) as isize;
|
||||
*size = match whence {
|
||||
SEEK_SET => cmp::min(len, pos),
|
||||
SEEK_CUR => {
|
||||
cmp::max(0, cmp::min(len, *size as isize + pos))
|
||||
}
|
||||
SEEK_END => {
|
||||
cmp::max(0, cmp::min(len, len + pos))
|
||||
}
|
||||
_ => return Err(Error::new(EINVAL)),
|
||||
} as usize;
|
||||
|
||||
Ok(Some(*size as isize))
|
||||
disk.as_ref().blocks * disk.as_ref().block_size
|
||||
}
|
||||
Handle::Partition(disk_num, part_num, ref mut size) => {
|
||||
Handle::Partition(disk_num, part_num) => {
|
||||
let disk = self.disks.get_mut(&disk_num).ok_or(Error::new(EBADF))?;
|
||||
let part = disk
|
||||
.pt
|
||||
@@ -494,22 +448,9 @@ impl SchemeBlockMut for DiskScheme {
|
||||
.get(part_num as usize)
|
||||
.ok_or(Error::new(EBADF))?;
|
||||
|
||||
let len = (part.size * disk.as_ref().block_size) as isize;
|
||||
|
||||
*size = match whence {
|
||||
SEEK_SET => cmp::min(len, pos),
|
||||
SEEK_CUR => {
|
||||
cmp::max(0, cmp::min(len, *size as isize + pos))
|
||||
}
|
||||
SEEK_END => {
|
||||
cmp::max(0, cmp::min(len, len + pos))
|
||||
}
|
||||
_ => return Err(Error::new(EINVAL)),
|
||||
} as usize;
|
||||
|
||||
Ok(Some(*size as isize))
|
||||
part.size * disk.as_ref().block_size
|
||||
}
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
fn close(&mut self, id: usize) -> Result<Option<usize>> {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
name = "usbscsid"
|
||||
version = "0.1.0"
|
||||
authors = ["4lDO2 <4lDO2@protonmail.com>"]
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
@@ -13,6 +13,6 @@ libredox = "0.1.3"
|
||||
plain = "0.2"
|
||||
redox-daemon = "0.1"
|
||||
redox_syscall = { version = "0.5", features = ["std"] }
|
||||
redox-scheme = { git = "https://gitlab.redox-os.org/4lDO2/redox-scheme.git", branch = "schemev2" }
|
||||
redox-scheme = { git = "https://gitlab.redox-os.org/4lDO2/redox-scheme.git", branch = "schemev2plus" }
|
||||
thiserror = "1"
|
||||
xhcid = { path = "../../xhcid" }
|
||||
|
||||
@@ -15,9 +15,9 @@ use syscall::flag::{SEEK_CUR, SEEK_END, SEEK_SET};
|
||||
const LIST_CONTENTS: &'static [u8] = b"0\n";
|
||||
|
||||
enum Handle {
|
||||
List(usize),
|
||||
Disk(usize),
|
||||
//Partition(usize, u32, usize),
|
||||
List,
|
||||
Disk,
|
||||
//Partition(usize, u32),
|
||||
}
|
||||
|
||||
pub struct ScsiScheme<'a> {
|
||||
@@ -50,12 +50,12 @@ impl SchemeMut for ScsiScheme<'_> {
|
||||
.trim_start_matches('/');
|
||||
let handle = if path_str.is_empty() {
|
||||
// List
|
||||
Handle::List(0)
|
||||
Handle::List
|
||||
} else if let Some(_p_pos) = path_str.chars().position(|c| c == 'p') {
|
||||
// TODO: Partitions.
|
||||
return Err(Error::new(ENOSYS));
|
||||
} else {
|
||||
Handle::Disk(0)
|
||||
Handle::Disk
|
||||
};
|
||||
self.next_fd += 1;
|
||||
self.handles.insert(self.next_fd, handle);
|
||||
@@ -63,13 +63,13 @@ impl SchemeMut for ScsiScheme<'_> {
|
||||
}
|
||||
fn fstat(&mut self, fd: usize, stat: &mut syscall::Stat) -> Result<usize> {
|
||||
match self.handles.get(&fd).ok_or(Error::new(EBADF))? {
|
||||
Handle::Disk(_) => {
|
||||
Handle::Disk => {
|
||||
stat.st_mode = MODE_CHR;
|
||||
stat.st_size = self.scsi.get_disk_size();
|
||||
stat.st_blksize = self.scsi.block_size;
|
||||
stat.st_blocks = self.scsi.block_count;
|
||||
}
|
||||
Handle::List(_) => {
|
||||
Handle::List => {
|
||||
stat.st_mode = MODE_DIR;
|
||||
stat.st_size = LIST_CONTENTS.len() as u64;
|
||||
}
|
||||
@@ -78,84 +78,64 @@ impl SchemeMut for ScsiScheme<'_> {
|
||||
}
|
||||
fn fpath(&mut self, fd: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
let path = match self.handles.get_mut(&fd).ok_or(Error::new(EBADF))? {
|
||||
Handle::Disk(_) => "0",
|
||||
Handle::List(_) => "",
|
||||
Handle::Disk => "0",
|
||||
Handle::List => "",
|
||||
}
|
||||
.as_bytes();
|
||||
let min = std::cmp::min(path.len(), buf.len());
|
||||
buf[..min].copy_from_slice(&path[..min]);
|
||||
Ok(min)
|
||||
}
|
||||
fn seek(&mut self, fd: usize, pos: isize, whence: usize) -> Result<isize> {
|
||||
match self.handles.get_mut(&fd).ok_or(Error::new(EBADF))? {
|
||||
Handle::Disk(ref mut offset) => {
|
||||
let len = self.scsi.get_disk_size() as isize;
|
||||
*offset = match whence {
|
||||
SEEK_SET => cmp::max(0, cmp::min(pos, len)),
|
||||
SEEK_CUR => cmp::max(0, cmp::min(*offset as isize + pos, len)),
|
||||
SEEK_END => cmp::max(0, cmp::min(len + pos, len)),
|
||||
_ => return Err(Error::new(EINVAL)),
|
||||
} as usize;
|
||||
Ok(*offset as isize)
|
||||
}
|
||||
Handle::List(ref mut offset) => {
|
||||
let len = LIST_CONTENTS.len() as isize;
|
||||
*offset = match whence {
|
||||
SEEK_SET => cmp::max(0, cmp::min(pos, len)),
|
||||
SEEK_CUR => cmp::max(0, cmp::min(*offset as isize + pos, len)),
|
||||
SEEK_END => cmp::max(0, cmp::min(len + pos, len)),
|
||||
_ => return Err(Error::new(EINVAL)),
|
||||
} as usize;
|
||||
Ok(*offset as isize)
|
||||
}
|
||||
}
|
||||
fn fsize(&mut self, fd: usize) -> Result<u64> {
|
||||
Ok(match self.handles.get_mut(&fd).ok_or(Error::new(EBADF))? {
|
||||
Handle::Disk => self.scsi.get_disk_size(),
|
||||
Handle::List => LIST_CONTENTS.len() as u64,
|
||||
})
|
||||
}
|
||||
fn read(&mut self, fd: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
fn read(&mut self, fd: usize, buf: &mut [u8], offset: u64, _fcntl_flags: u32) -> Result<usize> {
|
||||
match self.handles.get_mut(&fd).ok_or(Error::new(EBADF))? {
|
||||
Handle::Disk(ref mut offset) => {
|
||||
if *offset as u64 % u64::from(self.scsi.block_size) != 0
|
||||
Handle::Disk => {
|
||||
if offset % u64::from(self.scsi.block_size) != 0
|
||||
|| buf.len() as u64 % u64::from(self.scsi.block_size) != 0
|
||||
{
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
let lba = *offset as u64 / u64::from(self.scsi.block_size);
|
||||
let bytes_read = self
|
||||
.scsi
|
||||
.read(self.protocol, lba, buf)
|
||||
.map_err(|err| dbg!(err))
|
||||
.or(Err(Error::new(EIO)))?;
|
||||
*offset += bytes_read as usize;
|
||||
Ok(bytes_read as usize)
|
||||
let lba = offset / u64::from(self.scsi.block_size);
|
||||
match self.scsi.read(self.protocol, lba, buf) {
|
||||
Ok(bytes_read) => Ok(bytes_read as usize),
|
||||
Err(err) => {
|
||||
eprintln!("usbscsid: READ IO ERROR: {err}");
|
||||
Err(Error::new(EIO))
|
||||
}
|
||||
}
|
||||
}
|
||||
Handle::List(ref mut offset) => {
|
||||
let max_bytes_to_read = cmp::min(LIST_CONTENTS.len(), buf.len());
|
||||
let bytes_to_read = cmp::max(max_bytes_to_read, *offset) - *offset;
|
||||
Handle::List => {
|
||||
let src = usize::try_from(offset).ok().and_then(|o| LIST_CONTENTS.get(o..)).unwrap_or(&[]);
|
||||
let min = core::cmp::min(src.len(), buf.len());
|
||||
buf[..min].copy_from_slice(&src[..min]);
|
||||
|
||||
buf[..bytes_to_read].copy_from_slice(&LIST_CONTENTS[..bytes_to_read]);
|
||||
*offset += bytes_to_read;
|
||||
|
||||
Ok(bytes_to_read)
|
||||
Ok(min)
|
||||
}
|
||||
}
|
||||
}
|
||||
fn write(&mut self, fd: usize, buf: &[u8]) -> Result<usize> {
|
||||
fn write(&mut self, fd: usize, buf: &[u8], offset: u64, _fcntl_flags: u32) -> Result<usize> {
|
||||
match self.handles.get_mut(&fd).ok_or(Error::new(EBADF))? {
|
||||
Handle::Disk(ref mut offset) => {
|
||||
if *offset as u64 % u64::from(self.scsi.block_size) != 0
|
||||
Handle::Disk => {
|
||||
if offset % u64::from(self.scsi.block_size) != 0
|
||||
|| buf.len() as u64 % u64::from(self.scsi.block_size) != 0
|
||||
{
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
let lba = *offset as u64 / u64::from(self.scsi.block_size);
|
||||
let bytes_written = self
|
||||
.scsi
|
||||
.write(self.protocol, lba, buf)
|
||||
.map_err(|err| dbg!(err))
|
||||
.or(Err(Error::new(EIO)))?;
|
||||
*offset += bytes_written as usize;
|
||||
Ok(bytes_written as usize)
|
||||
let lba = offset / u64::from(self.scsi.block_size);
|
||||
match self.scsi.write(self.protocol, lba, buf) {
|
||||
Ok(bytes_written) => Ok(bytes_written as usize),
|
||||
Err(err) => {
|
||||
eprintln!("usbscsid: WRITE IO ERROR: {err}");
|
||||
Err(Error::new(EIO))
|
||||
}
|
||||
}
|
||||
}
|
||||
Handle::List(_) => Err(Error::new(EBADF)),
|
||||
Handle::List => Err(Error::new(EBADF)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ spin = "*"
|
||||
redox-daemon = "0.1"
|
||||
redox-log = "0.1"
|
||||
redox_syscall = { version = "0.5", features = ["std"] }
|
||||
redox-scheme = { git = "https://gitlab.redox-os.org/4lDO2/redox-scheme.git", branch = "schemev2" }
|
||||
redox-scheme = { git = "https://gitlab.redox-os.org/4lDO2/redox-scheme.git", branch = "schemev2plus" }
|
||||
partitionlib = { git = "https://gitlab.redox-os.org/redox-os/partitionlib.git" }
|
||||
|
||||
common = { path = "../../common" }
|
||||
|
||||
@@ -84,18 +84,13 @@ pub enum Handle {
|
||||
Partition {
|
||||
/// Partition Number
|
||||
number: u32,
|
||||
/// Offset in bytes
|
||||
offset: usize,
|
||||
},
|
||||
|
||||
List {
|
||||
entries: Vec<u8>,
|
||||
offset: usize,
|
||||
},
|
||||
|
||||
Disk {
|
||||
offset: usize,
|
||||
},
|
||||
Disk,
|
||||
}
|
||||
|
||||
pub struct DiskScheme<'a> {
|
||||
@@ -224,7 +219,6 @@ impl<'a> SchemeBlockMut for DiskScheme<'a> {
|
||||
id,
|
||||
Handle::List {
|
||||
entries: list.into_bytes(),
|
||||
offset: 0,
|
||||
},
|
||||
);
|
||||
|
||||
@@ -250,7 +244,6 @@ impl<'a> SchemeBlockMut for DiskScheme<'a> {
|
||||
id,
|
||||
Handle::Partition {
|
||||
number: part_num,
|
||||
offset: 0,
|
||||
},
|
||||
);
|
||||
|
||||
@@ -261,25 +254,24 @@ impl<'a> SchemeBlockMut for DiskScheme<'a> {
|
||||
|
||||
let id = self.next_id;
|
||||
self.next_id += 1;
|
||||
self.handles.insert(id, Handle::Disk { offset: 0 });
|
||||
self.handles.insert(id, Handle::Disk);
|
||||
Ok(Some(id))
|
||||
}
|
||||
}
|
||||
|
||||
fn read(&mut self, id: usize, buf: &mut [u8]) -> syscall::Result<Option<usize>> {
|
||||
match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
|
||||
fn read(&mut self, id: usize, buf: &mut [u8], offset: u64, _fcntl_flags: u32) -> syscall::Result<Option<usize>> {
|
||||
Ok(Some(match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
|
||||
Handle::List {
|
||||
ref mut entries,
|
||||
ref mut offset,
|
||||
} => {
|
||||
let count = (&entries[*offset..]).read(buf).unwrap();
|
||||
*offset += count;
|
||||
Ok(Some(count))
|
||||
let src = usize::try_from(offset).ok().and_then(|o| entries.get(o..)).unwrap_or(&[]);
|
||||
let count = core::cmp::min(src.len(), buf.len());
|
||||
buf[..count].copy_from_slice(&src[..count]);
|
||||
count
|
||||
}
|
||||
|
||||
Handle::Partition {
|
||||
number,
|
||||
ref mut offset,
|
||||
} => {
|
||||
let part_table = self.part_table.as_ref().unwrap();
|
||||
let part = part_table
|
||||
@@ -288,68 +280,52 @@ impl<'a> SchemeBlockMut for DiskScheme<'a> {
|
||||
.ok_or(Error::new(EBADF))?;
|
||||
|
||||
// Get the offset in sectors.
|
||||
let rel_block = (*offset as u64) / BLK_SIZE;
|
||||
let rel_block = offset / BLK_SIZE;
|
||||
// if rel_block >= part.size {
|
||||
// return Err(Error::new(EOVERFLOW));
|
||||
// }
|
||||
|
||||
let abs_block = part.start_lba + rel_block;
|
||||
|
||||
let count = futures::executor::block_on(self.queue.read(abs_block, buf));
|
||||
*offset += count;
|
||||
Ok(Some(count))
|
||||
futures::executor::block_on(self.queue.read(abs_block, buf))
|
||||
}
|
||||
|
||||
Handle::Disk { ref mut offset } => {
|
||||
Handle::Disk => {
|
||||
let block_size = self.cfg.block_size();
|
||||
|
||||
let count = futures::executor::block_on(
|
||||
self.queue.read((*offset as u64) / block_size as u64, buf),
|
||||
);
|
||||
*offset += count;
|
||||
Ok(Some(count))
|
||||
futures::executor::block_on(
|
||||
self.queue.read(offset / u64::from(block_size), buf),
|
||||
)
|
||||
}
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
fn write(&mut self, id: usize, buf: &[u8]) -> syscall::Result<Option<usize>> {
|
||||
match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
|
||||
Handle::Disk { ref mut offset } => {
|
||||
fn write(&mut self, id: usize, buf: &[u8], offset: u64, _fcntl_flags: u32) -> syscall::Result<Option<usize>> {
|
||||
Ok(Some(match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
|
||||
Handle::Disk => {
|
||||
let block_size = self.cfg.block_size();
|
||||
let count = futures::executor::block_on(
|
||||
self.queue.write((*offset as u64) / block_size as u64, buf),
|
||||
);
|
||||
|
||||
*offset += count;
|
||||
Ok(Some(count))
|
||||
futures::executor::block_on(
|
||||
self.queue.write(offset / u64::from(block_size), buf),
|
||||
)
|
||||
}
|
||||
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
_ => todo!(),
|
||||
}))
|
||||
}
|
||||
|
||||
fn seek(&mut self, id: usize, pos: isize, whence: usize) -> syscall::Result<Option<isize>> {
|
||||
match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
|
||||
fn fsize(&mut self, id: usize) -> syscall::Result<Option<u64>> {
|
||||
Ok(Some(match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
|
||||
Handle::List {
|
||||
ref entries,
|
||||
ref mut offset,
|
||||
} => {
|
||||
let len = entries.len() as isize;
|
||||
log::debug!("list: whence={whence:?} pos={pos:?} part_len={len:?}");
|
||||
let len = entries.len() as u64;
|
||||
log::debug!("list: part_len={len:?}");
|
||||
|
||||
*offset = match whence {
|
||||
SEEK_SET => cmp::min(len, pos),
|
||||
SEEK_CUR => cmp::max(0, cmp::min(len, *offset as isize + pos)),
|
||||
SEEK_END => cmp::max(0, cmp::min(len, len + pos)),
|
||||
_ => return Err(Error::new(EINVAL)),
|
||||
} as usize;
|
||||
|
||||
Ok(Some(*offset as isize))
|
||||
len
|
||||
}
|
||||
|
||||
Handle::Partition {
|
||||
number,
|
||||
ref mut offset,
|
||||
} => {
|
||||
let part_table = self.part_table.as_ref().unwrap();
|
||||
let part = part_table
|
||||
@@ -358,33 +334,15 @@ impl<'a> SchemeBlockMut for DiskScheme<'a> {
|
||||
.ok_or(Error::new(EBADF))?;
|
||||
|
||||
// Partition size in bytes.
|
||||
let len = (part.size * BLK_SIZE) as isize;
|
||||
let len = part.size * BLK_SIZE;
|
||||
|
||||
log::debug!("part: whence={whence:?} pos={pos:?} part_len={len:?}");
|
||||
log::debug!("part: part_len={len:?}");
|
||||
|
||||
*offset = match whence {
|
||||
SEEK_SET => cmp::min(len, pos),
|
||||
SEEK_CUR => cmp::max(0, cmp::min(len, *offset as isize + pos)),
|
||||
SEEK_END => cmp::max(0, cmp::min(len, len + pos)),
|
||||
_ => return Err(Error::new(EINVAL)),
|
||||
} as usize;
|
||||
|
||||
Ok(Some(*offset as isize))
|
||||
len
|
||||
}
|
||||
|
||||
Handle::Disk { ref mut offset } => {
|
||||
let len = (self.cfg.capacity() * self.cfg.block_size() as u64) as isize;
|
||||
|
||||
*offset = match whence {
|
||||
SEEK_SET => cmp::min(len, pos),
|
||||
SEEK_CUR => cmp::max(0, cmp::min(len, *offset as isize + pos)),
|
||||
SEEK_END => cmp::max(0, cmp::min(len, len + pos)),
|
||||
_ => return Err(Error::new(EINVAL)),
|
||||
} as usize;
|
||||
|
||||
Ok(Some(*offset as isize))
|
||||
}
|
||||
}
|
||||
Handle::Disk => self.cfg.capacity() * u64::from(self.cfg.block_size()),
|
||||
}))
|
||||
}
|
||||
|
||||
fn fpath(&mut self, _id: usize, _buf: &mut [u8]) -> syscall::Result<Option<usize>> {
|
||||
|
||||
Reference in New Issue
Block a user