📄 file_storage.c
字号:
curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
return -EINVAL;
}
if (fsg->cmnd[1] & 0x08) // FUA
curlun->filp->f_flags |= O_SYNC;
}
if (lba >= curlun->num_sectors) {
curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
return -EINVAL;
}
/* Carry out the file writes */
get_some_more = 1;
file_offset = usb_offset = ((loff_t) lba) << 9;
amount_left_to_req = amount_left_to_write = fsg->data_size_from_cmnd;
while (amount_left_to_write > 0) {
/* Queue a request for more data from the host */
bh = fsg->next_buffhd_to_fill;
if (bh->state == BUF_STATE_EMPTY && get_some_more) {
/* Figure out how much we want to get:
* Try to get the remaining amount.
* But don't get more than the buffer size.
* And don't try to go past the end of the file.
* If we're not at a page boundary,
* don't go past the next page.
* If this means getting 0, then we were asked
* to write past the end of file.
* Finally, round down to a block boundary. */
amount = min(amount_left_to_req, mod_data.buflen);
amount = min((loff_t) amount, curlun->file_length -
usb_offset);
partial_page = usb_offset & (PAGE_CACHE_SIZE - 1);
if (partial_page > 0)
amount = min(amount,
(unsigned int) PAGE_CACHE_SIZE - partial_page);
if (amount == 0) {
get_some_more = 0;
curlun->sense_data =
SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
curlun->sense_data_info = usb_offset >> 9;
continue;
}
amount -= (amount & 511);
if (amount == 0) {
/* Why were we were asked to transfer a
* partial block? */
get_some_more = 0;
continue;
}
/* Get the next buffer */
usb_offset += amount;
fsg->usb_amount_left -= amount;
amount_left_to_req -= amount;
if (amount_left_to_req == 0)
get_some_more = 0;
/* amount is always divisible by 512, hence by
* the bulk-out maxpacket size */
bh->outreq->length = bh->bulk_out_intended_length =
amount;
start_transfer(fsg, fsg->bulk_out, bh->outreq,
&bh->outreq_busy, &bh->state);
fsg->next_buffhd_to_fill = bh->next;
continue;
}
/* Write the received data to the backing file */
bh = fsg->next_buffhd_to_drain;
if (bh->state == BUF_STATE_EMPTY && !get_some_more)
break; // We stopped early
if (bh->state == BUF_STATE_FULL) {
fsg->next_buffhd_to_drain = bh->next;
bh->state = BUF_STATE_EMPTY;
/* Did something go wrong with the transfer? */
if (bh->outreq->status != 0) {
curlun->sense_data = SS_COMMUNICATION_FAILURE;
curlun->sense_data_info = file_offset >> 9;
break;
}
amount = bh->outreq->actual;
if (curlun->file_length - file_offset < amount) {
LERROR(curlun,
"write %u @ %llu beyond end %llu\n",
amount, (unsigned long long) file_offset,
(unsigned long long) curlun->file_length);
amount = curlun->file_length - file_offset;
}
/* Perform the write */
file_offset_tmp = file_offset;
nwritten = vfs_write(curlun->filp,
(char __user *) bh->buf,
amount, &file_offset_tmp);
VLDBG(curlun, "file write %u @ %llu -> %d\n", amount,
(unsigned long long) file_offset,
(int) nwritten);
if (signal_pending(current))
return -EINTR; // Interrupted!
if (nwritten < 0) {
LDBG(curlun, "error in file write: %d\n",
(int) nwritten);
nwritten = 0;
} else if (nwritten < amount) {
LDBG(curlun, "partial file write: %d/%u\n",
(int) nwritten, amount);
nwritten -= (nwritten & 511);
// Round down to a block
}
file_offset += nwritten;
amount_left_to_write -= nwritten;
fsg->residue -= nwritten;
/* If an error occurred, report it and its position */
if (nwritten < amount) {
curlun->sense_data = SS_WRITE_ERROR;
curlun->sense_data_info = file_offset >> 9;
break;
}
/* Did the host decide to stop early? */
if (bh->outreq->actual != bh->outreq->length) {
fsg->short_packet_received = 1;
break;
}
continue;
}
/* Wait for something to happen */
if ((rc = sleep_thread(fsg)) != 0)
return rc;
}
return -EIO; // No default reply
}
/*-------------------------------------------------------------------------*/
/* Sync the file data, don't bother with the metadata.
* This code was copied from fs/buffer.c:sys_fdatasync(). */
static int fsync_sub(struct lun *curlun)
{
struct file *filp = curlun->filp;
struct inode *inode;
int rc, err;
if (curlun->ro || !filp)
return 0;
if (!filp->f_op->fsync)
return -EINVAL;
inode = filp->f_dentry->d_inode;
down(&inode->i_sem);
current->flags |= PF_SYNCWRITE;
rc = filemap_fdatawrite(inode->i_mapping);
err = filp->f_op->fsync(filp, filp->f_dentry, 1);
if (!rc)
rc = err;
err = filemap_fdatawait(inode->i_mapping);
if (!rc)
rc = err;
current->flags &= ~PF_SYNCWRITE;
up(&inode->i_sem);
VLDBG(curlun, "fdatasync -> %d\n", rc);
return rc;
}
static void fsync_all(struct fsg_dev *fsg)
{
int i;
for (i = 0; i < fsg->nluns; ++i)
fsync_sub(&fsg->luns[i]);
}
static int do_synchronize_cache(struct fsg_dev *fsg)
{
struct lun *curlun = fsg->curlun;
int rc;
/* We ignore the requested LBA and write out all file's
* dirty data buffers. */
rc = fsync_sub(curlun);
if (rc)
curlun->sense_data = SS_WRITE_ERROR;
return 0;
}
/*-------------------------------------------------------------------------*/
static void invalidate_sub(struct lun *curlun)
{
struct file *filp = curlun->filp;
struct inode *inode = filp->f_dentry->d_inode;
unsigned long rc;
rc = invalidate_inode_pages(inode->i_mapping);
VLDBG(curlun, "invalidate_inode_pages -> %ld\n", rc);
}
static int do_verify(struct fsg_dev *fsg)
{
struct lun *curlun = fsg->curlun;
u32 lba;
u32 verification_length;
struct fsg_buffhd *bh = fsg->next_buffhd_to_fill;
loff_t file_offset, file_offset_tmp;
u32 amount_left;
unsigned int amount;
ssize_t nread;
/* Get the starting Logical Block Address and check that it's
* not too big */
lba = get_be32(&fsg->cmnd[2]);
if (lba >= curlun->num_sectors) {
curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
return -EINVAL;
}
/* We allow DPO (Disable Page Out = don't save data in the
* cache) but we don't implement it. */
if ((fsg->cmnd[1] & ~0x10) != 0) {
curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
return -EINVAL;
}
verification_length = get_be16(&fsg->cmnd[7]);
if (unlikely(verification_length == 0))
return -EIO; // No default reply
/* Prepare to carry out the file verify */
amount_left = verification_length << 9;
file_offset = ((loff_t) lba) << 9;
/* Write out all the dirty buffers before invalidating them */
fsync_sub(curlun);
if (signal_pending(current))
return -EINTR;
invalidate_sub(curlun);
if (signal_pending(current))
return -EINTR;
/* Just try to read the requested blocks */
while (amount_left > 0) {
/* Figure out how much we need to read:
* Try to read the remaining amount, but not more than
* the buffer size.
* And don't try to read past the end of the file.
* If this means reading 0 then we were asked to read
* past the end of file. */
amount = min((unsigned int) amount_left, mod_data.buflen);
amount = min((loff_t) amount,
curlun->file_length - file_offset);
if (amount == 0) {
curlun->sense_data =
SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
curlun->sense_data_info = file_offset >> 9;
break;
}
/* Perform the read */
file_offset_tmp = file_offset;
nread = vfs_read(curlun->filp,
(char __user *) bh->buf,
amount, &file_offset_tmp);
VLDBG(curlun, "file read %u @ %llu -> %d\n", amount,
(unsigned long long) file_offset,
(int) nread);
if (signal_pending(current))
return -EINTR;
if (nread < 0) {
LDBG(curlun, "error in file verify: %d\n",
(int) nread);
nread = 0;
} else if (nread < amount) {
LDBG(curlun, "partial file verify: %d/%u\n",
(int) nread, amount);
nread -= (nread & 511); // Round down to a sector
}
if (nread == 0) {
curlun->sense_data = SS_UNRECOVERED_READ_ERROR;
curlun->sense_data_info = file_offset >> 9;
break;
}
file_offset += nread;
amount_left -= nread;
}
return 0;
}
/*-------------------------------------------------------------------------*/
static int do_inquiry(struct fsg_dev *fsg, struct fsg_buffhd *bh)
{
u8 *buf = (u8 *) bh->buf;
static char vendor_id[] = "Linux ";
static char product_id[] = "File-Stor Gadget";
if (!fsg->curlun) { // Unsupported LUNs are okay
fsg->bad_lun_okay = 1;
memset(buf, 0, 36);
buf[0] = 0x7f; // Unsupported, no device-type
return 36;
}
memset(buf, 0, 8); // Non-removable, direct-access device
if (mod_data.removable)
buf[1] = 0x80;
buf[2] = 2; // ANSI SCSI level 2
buf[3] = 2; // SCSI-2 INQUIRY data format
buf[4] = 31; // Additional length
// No special options
sprintf(buf + 8, "%-8s%-16s%04x", vendor_id, product_id,
mod_data.release);
return 36;
}
static int do_request_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh)
{
struct lun *curlun = fsg->curlun;
u8 *buf = (u8 *) bh->buf;
u32 sd, sdinfo;
/*
* From the SCSI-2 spec., section 7.9 (Unit attention condition):
*
* If a REQUEST SENSE command is received from an initiator
* with a pending unit attention condition (before the target
* generates the contingent allegiance condition), then the
* target shall either:
* a) report any pending sense data and preserve the unit
* attention condition on the logical unit, or,
* b) report the unit attention condition, may discard any
* pending sense data, and clear the unit attention
* condition on the logical unit for that initiator.
*
* FSG normally uses option a); enable this code to use option b).
*/
#if 0
if (curlun && curlun->unit_attention_data != SS_NO_SENSE) {
curlun->sense_data = curlun->unit_attention_data;
curlun->unit_attention_data = SS_NO_SENSE;
}
#endif
if (!curlun) { // Unsupported LUNs are okay
fsg->bad_lun_okay = 1;
sd = SS_LOGICAL_UNIT_NOT_SUPPORTED;
sdinfo = 0;
} else {
sd = curlun->sense_data;
sdinfo = curlun->sense_data_info;
curlun->sense_data = SS_NO_SENSE;
curlun->sense_data_info = 0;
}
memset(buf, 0, 18);
buf[0] = 0x80 | 0x70; // Valid, current error
buf[2] = SK(sd);
put_be32(&buf[3], sdinfo); // Sense information
buf[7] = 18 - 8; // Additional sense length
buf[12] = ASC(sd);
buf[13] = ASCQ(sd);
return 18;
}
static int do_read_capacity(struct fsg_dev *fsg, struct fsg_buffhd *bh)
{
struct lun *curlun = fsg->curlun;
u32 lba = get_be32(&fsg->cmnd[2]);
int pmi = fsg->cmnd[8];
u8 *buf = (u8 *) bh->buf;
/* Check the PMI and LBA fields */
if (pmi > 1 || (pmi == 0 && lba != 0)) {
curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
return -EINVAL;
}
put_be32(&buf[0], curlun->num_sectors - 1); // Max logical block
put_be32(&buf[4], 512); // Block length
return 8;
}
static int do_mode_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh)
{
struct lun *curlun = fsg->curlun;
int mscmnd = fsg->cmnd[0];
u8 *buf = (u8 *) bh->buf;
u8 *buf0 = buf;
int pc, page_code;
int changeable_values, all_pages;
int valid_page = 0;
int len, limit;
if ((fsg->cmnd[1] & ~0x08) != 0) { // Mask away DBD
curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
return -EINVAL;
}
pc = fsg->cmnd[2] >> 6;
page_code = fsg->cmnd[2] & 0x3f;
if (pc == 3) {
curlun->sense_data = SS_SAVING_PARAMETERS_NOT_SUPPORTED;
return -EINVAL;
}
changeable_values = (pc == 1);
all_pages = (page_code == 0x3f);
/* Write the mode parameter header. Fixed values are: default
* medium type, no cache control (DPOFUA), and no block descriptors.
* The only variable value is t
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -