mirror of
https://gitee.com/openharmony/third_party_f2fs-tools
synced 2024-11-23 01:59:54 +00:00
!41 f2fs-tools upgrade version 1.16.0
Merge pull request !41 from shenshensun/f2fs_tools_v1.16
This commit is contained in:
commit
a7e97206a7
2
.gitignore
vendored
2
.gitignore
vendored
@ -46,12 +46,10 @@ stamp-h1
|
||||
|
||||
/mkfs/mkfs.f2fs
|
||||
/fsck/fsck.f2fs
|
||||
/tools/f2fstat
|
||||
/tools/fibmap.f2fs
|
||||
/tools/parse.f2fs
|
||||
/tools/f2fscrypt
|
||||
/tools/f2fs_io/f2fs_io
|
||||
/tools/sg_write_buffer/sg_write_buffer
|
||||
|
||||
# cscope files
|
||||
cscope.*
|
||||
|
94
configure.ac
94
configure.ac
@ -39,12 +39,28 @@ AM_INIT_AUTOMAKE([-Wall -Werror foreign tar-pax dist-xz])
|
||||
|
||||
# Test configure options.
|
||||
AC_ARG_WITH([selinux],
|
||||
AS_HELP_STRING([--without-selinux],
|
||||
[Ignore presence of libselinux and disable selinux support]))
|
||||
[AS_HELP_STRING([--without-selinux],
|
||||
[Ignore presence of libselinux and disable selinux support])],
|
||||
[],
|
||||
[with_selinux=check])
|
||||
|
||||
AC_ARG_WITH([blkid],
|
||||
AS_HELP_STRING([--without-blkid],
|
||||
[Ignore presence of libblkid and disable blkid support]))
|
||||
[AS_HELP_STRING([--without-blkid],
|
||||
[Ignore presence of libblkid and disable blkid support])],
|
||||
[],
|
||||
[with_blkid=check])
|
||||
|
||||
AC_ARG_WITH([lzo2],
|
||||
[AS_HELP_STRING([--without-lzo2],
|
||||
[Ignore presence of liblzo2 and disable lzo2 support])],
|
||||
[],
|
||||
[with_lzo2=check])
|
||||
|
||||
AC_ARG_WITH([lz4],
|
||||
[AS_HELP_STRING([--without-lz4],
|
||||
[Ignore presence of liblz4 and disable lz4 support])],
|
||||
[],
|
||||
[with_lz4=check])
|
||||
|
||||
# Checks for programs.
|
||||
AC_PROG_CC
|
||||
@ -55,29 +71,53 @@ AC_PATH_PROG([LDCONFIG], [ldconfig],
|
||||
[$PATH:/sbin])
|
||||
|
||||
# Checks for libraries.
|
||||
AC_CHECK_LIB([blkid], [blkid_probe_all],
|
||||
[AC_SUBST([libblkid_LIBS], ["-lblkid"])
|
||||
AC_DEFINE([HAVE_LIBBLKID], [1],
|
||||
[Define if you have libblkid])
|
||||
], [], [])
|
||||
AS_IF([test "x$with_blkid" != xno],
|
||||
[AC_CHECK_LIB([blkid], [blkid_probe_all],
|
||||
[AC_SUBST([libblkid_LIBS], ["-lblkid"])
|
||||
AC_DEFINE([HAVE_LIBBLKID], [1],
|
||||
[Define if you have libblkid])
|
||||
],
|
||||
[if test "x$with_blkid" != xcheck; then
|
||||
AC_MSG_FAILURE(
|
||||
[--with-blkid was given, but test for blkid failed])
|
||||
fi
|
||||
], -lblkid)])
|
||||
|
||||
AC_CHECK_LIB([lzo2], [main],
|
||||
[AC_SUBST([liblzo2_LIBS], ["-llzo2"])
|
||||
AC_DEFINE([HAVE_LIBLZO2], [1],
|
||||
[Define if you have liblzo2])
|
||||
], [], [])
|
||||
AS_IF([test "x$with_lzo2" != xno],
|
||||
[AC_CHECK_LIB([lzo2], [main],
|
||||
[AC_SUBST([liblzo2_LIBS], ["-llzo2"])
|
||||
AC_DEFINE([HAVE_LIBLZO2], [1],
|
||||
[Define if you have liblzo2])
|
||||
],
|
||||
[if test "x$with_lzo2" != xcheck; then
|
||||
AC_MSG_FAILURE(
|
||||
[--with-lzo2 was given, but test for lzo2 failed])
|
||||
fi
|
||||
], -llzo2)])
|
||||
|
||||
AC_CHECK_LIB([lz4], [main],
|
||||
[AC_SUBST([liblz4_LIBS], ["-llz4"])
|
||||
AC_DEFINE([HAVE_LIBLZ4], [1],
|
||||
[Define if you have liblz4])
|
||||
], [], [])
|
||||
AS_IF([test "x$with_lz4" != xno],
|
||||
[AC_CHECK_LIB([lz4], [main],
|
||||
[AC_SUBST([liblz4_LIBS], ["-llz4"])
|
||||
AC_DEFINE([HAVE_LIBLZ4], [1],
|
||||
[Define if you have liblz4])
|
||||
],
|
||||
[if test "x$with_lz4" != xcheck; then
|
||||
AC_MSG_FAILURE(
|
||||
[--with-lz4 was given, but test for lz4 failed])
|
||||
fi
|
||||
], -llz4)])
|
||||
|
||||
AC_CHECK_LIB([selinux], [getcon],
|
||||
[AC_SUBST([libselinux_LIBS], ["-lselinux"])
|
||||
AC_DEFINE([HAVE_LIBSELINUX], [1],
|
||||
[Define if you have libselinux])
|
||||
], [], [])
|
||||
AS_IF([test "x$with_selinux" != xno],
|
||||
[AC_CHECK_LIB([selinux], [getcon],
|
||||
[AC_SUBST([libselinux_LIBS], ["-lselinux"])
|
||||
AC_DEFINE([HAVE_LIBSELINUX], [1],
|
||||
[Define if you have libselinux])
|
||||
],
|
||||
[if test "x$with_selinux" != xcheck; then
|
||||
AC_MSG_FAILURE(
|
||||
[--with-selinux was given, but test for selinux failed])
|
||||
fi
|
||||
], -lselinux)])
|
||||
|
||||
AC_CHECK_LIB([uuid], [uuid_clear],
|
||||
[AC_SUBST([libuuid_LIBS], ["-luuid"])
|
||||
@ -151,6 +191,7 @@ AC_CHECK_FUNCS_ONCE([
|
||||
lseek64
|
||||
memset
|
||||
setmntent
|
||||
clock_gettime
|
||||
])
|
||||
|
||||
AS_IF([test "$ac_cv_header_byteswap_h" = "yes"],
|
||||
@ -219,7 +260,6 @@ AC_CONFIG_FILES([
|
||||
mkfs/Makefile
|
||||
fsck/Makefile
|
||||
tools/Makefile
|
||||
tools/sg_write_buffer/Makefile
|
||||
tools/f2fs_io/Makefile
|
||||
])
|
||||
|
||||
@ -234,12 +274,12 @@ AC_CHECK_MEMBER([struct blk_zone.capacity],
|
||||
]])
|
||||
|
||||
# export library version info for mkfs/libf2fs_format_la
|
||||
AC_SUBST(FMT_CURRENT, 8)
|
||||
AC_SUBST(FMT_CURRENT, 9)
|
||||
AC_SUBST(FMT_REVISION, 0)
|
||||
AC_SUBST(FMT_AGE, 0)
|
||||
|
||||
# export library version info for lib/libf2fs_la
|
||||
AC_SUBST(LIBF2FS_CURRENT, 9)
|
||||
AC_SUBST(LIBF2FS_CURRENT, 10)
|
||||
AC_SUBST(LIBF2FS_REVISION, 0)
|
||||
AC_SUBST(LIBF2FS_AGE, 0)
|
||||
|
||||
|
@ -32,10 +32,7 @@
|
||||
#ifdef HAVE_LIBLZ4
|
||||
#define LZ4_MEMORY_USAGE 14
|
||||
#define LZ4_MAX_INPUT_SIZE 0x7E000000 /* 2 113 929 216 bytes */
|
||||
#ifndef LZ4_STREAMSIZE
|
||||
#define LZ4_STREAMSIZE (LZ4_STREAMSIZE_U64 * sizeof(long long))
|
||||
#endif
|
||||
#define LZ4_MEM_COMPRESS LZ4_STREAMSIZE
|
||||
#define LZ4_MEM_COMPRESS sizeof(LZ4_stream_t)
|
||||
#define LZ4_ACCELERATION_DEFAULT 1
|
||||
#define LZ4_WORK_SIZE ALIGN_UP(LZ4_MEM_COMPRESS, 8)
|
||||
#endif
|
||||
|
@ -20,7 +20,6 @@
|
||||
|
||||
#define DICT_NODEBUG
|
||||
|
||||
#include "config.h"
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#ifdef DICT_NODEBUG
|
||||
|
33
fsck/dump.c
33
fsck/dump.c
@ -539,6 +539,39 @@ static bool is_sit_bitmap_set(struct f2fs_sb_info *sbi, u32 blk_addr)
|
||||
(const char *)se->cur_valid_map) != 0;
|
||||
}
|
||||
|
||||
void dump_node_scan_disk(struct f2fs_sb_info *sbi, nid_t nid)
|
||||
{
|
||||
struct f2fs_node *node_blk;
|
||||
pgoff_t blkaddr;
|
||||
int ret;
|
||||
pgoff_t start_blkaddr = SM_I(sbi)->main_blkaddr;
|
||||
pgoff_t end_blkaddr = start_blkaddr +
|
||||
(SM_I(sbi)->main_segments << sbi->log_blocks_per_seg);
|
||||
|
||||
node_blk = calloc(BLOCK_SZ, 1);
|
||||
ASSERT(node_blk);
|
||||
MSG(0, "Info: scan all nid: %u from block_addr [%lu: %lu]\n",
|
||||
nid, start_blkaddr, end_blkaddr);
|
||||
|
||||
for (blkaddr = start_blkaddr; blkaddr < end_blkaddr; blkaddr++) {
|
||||
struct seg_entry *se = get_seg_entry(sbi, GET_SEGNO(sbi, blkaddr));
|
||||
if (se->type < CURSEG_HOT_NODE)
|
||||
continue;
|
||||
|
||||
ret = dev_read_block(node_blk, blkaddr);
|
||||
ASSERT(ret >= 0);
|
||||
if (le32_to_cpu(node_blk->footer.ino) != nid ||
|
||||
le32_to_cpu(node_blk->footer.nid) != nid)
|
||||
continue;
|
||||
MSG(0, "Info: nid: %u, blkaddr: %lu\n", nid, blkaddr);
|
||||
MSG(0, "node_blk.footer.flag [0x%x]\n", le32_to_cpu(node_blk->footer.flag));
|
||||
MSG(0, "node_blk.footer.cp_ver [%x]\n", (u32)(cpver_of_node(node_blk)));
|
||||
print_inode_info(sbi, node_blk, 0);
|
||||
}
|
||||
|
||||
free(node_blk);
|
||||
}
|
||||
|
||||
int dump_node(struct f2fs_sb_info *sbi, nid_t nid, int force)
|
||||
{
|
||||
struct node_info ni;
|
||||
|
13
fsck/f2fs.h
13
fsck/f2fs.h
@ -20,6 +20,8 @@
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <f2fs_fs.h>
|
||||
|
||||
#ifdef HAVE_MNTENT_H
|
||||
#include <mntent.h>
|
||||
#endif
|
||||
@ -35,8 +37,6 @@
|
||||
#endif
|
||||
#include <assert.h>
|
||||
|
||||
#include "f2fs_fs.h"
|
||||
|
||||
#define EXIT_ERR_CODE (-1)
|
||||
#define ver_after(a, b) (typecheck(unsigned long long, a) && \
|
||||
typecheck(unsigned long long, b) && \
|
||||
@ -416,10 +416,13 @@ static inline block_t __start_sum_addr(struct f2fs_sb_info *sbi)
|
||||
|
||||
static inline block_t __end_block_addr(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
block_t end = SM_I(sbi)->main_blkaddr;
|
||||
return end + le64_to_cpu(F2FS_RAW_SUPER(sbi)->block_count);
|
||||
return SM_I(sbi)->main_blkaddr +
|
||||
(le32_to_cpu(F2FS_RAW_SUPER(sbi)->segment_count_main) <<
|
||||
sbi->log_blocks_per_seg);
|
||||
}
|
||||
|
||||
#define BLKS_PER_SEC(sbi) \
|
||||
((sbi)->segs_per_sec * (sbi)->blocks_per_seg)
|
||||
#define GET_ZONENO_FROM_SEGNO(sbi, segno) \
|
||||
((segno / sbi->segs_per_sec) / sbi->secs_per_zone)
|
||||
|
||||
@ -460,6 +463,7 @@ static inline block_t __end_block_addr(struct f2fs_sb_info *sbi)
|
||||
#define GET_R2L_SEGNO(sbi, segno) (segno + FREE_I_START_SEGNO(sbi))
|
||||
|
||||
#define MAIN_SEGS(sbi) (SM_I(sbi)->main_segments)
|
||||
#define TOTAL_SEGS(sbi) (SM_I(sbi)->segment_count)
|
||||
#define TOTAL_BLKS(sbi) (TOTAL_SEGS(sbi) << (sbi)->log_blocks_per_seg)
|
||||
#define MAX_BLKADDR(sbi) (SEG0_BLKADDR(sbi) + TOTAL_BLKS(sbi))
|
||||
|
||||
@ -508,7 +512,6 @@ struct fsync_inode_entry {
|
||||
((segno) % sit_i->sents_per_block)
|
||||
#define SIT_BLOCK_OFFSET(sit_i, segno) \
|
||||
((segno) / SIT_ENTRY_PER_BLOCK)
|
||||
#define TOTAL_SEGS(sbi) (SM_I(sbi)->main_segments)
|
||||
|
||||
static inline bool IS_VALID_NID(struct f2fs_sb_info *sbi, u32 nid)
|
||||
{
|
||||
|
62
fsck/fsck.c
62
fsck/fsck.c
@ -706,13 +706,16 @@ void fsck_chk_inode_blk(struct f2fs_sb_info *sbi, u32 nid,
|
||||
int ofs;
|
||||
char *en;
|
||||
u32 namelen;
|
||||
unsigned int idx = 0;
|
||||
unsigned int addrs, idx = 0;
|
||||
unsigned short i_gc_failures;
|
||||
int need_fix = 0;
|
||||
int ret;
|
||||
u32 cluster_size = 1 << node_blk->i.i_log_cluster_size;
|
||||
|
||||
if (!compr_supported && compressed) {
|
||||
if (!compressed)
|
||||
goto check_next;
|
||||
|
||||
if (!compr_supported || (node_blk->i.i_inline & F2FS_INLINE_DATA)) {
|
||||
/*
|
||||
* The 'compression' flag in i_flags affects the traverse of
|
||||
* the node tree. Thus, it must be fixed unconditionally
|
||||
@ -727,6 +730,7 @@ void fsck_chk_inode_blk(struct f2fs_sb_info *sbi, u32 nid,
|
||||
}
|
||||
i_flags &= ~F2FS_COMPR_FL;
|
||||
}
|
||||
check_next:
|
||||
memset(&child, 0, sizeof(child));
|
||||
child.links = 2;
|
||||
child.p_ino = nid;
|
||||
@ -738,8 +742,10 @@ void fsck_chk_inode_blk(struct f2fs_sb_info *sbi, u32 nid,
|
||||
|
||||
if (ftype == F2FS_FT_DIR) {
|
||||
f2fs_set_main_bitmap(sbi, ni->blk_addr, CURSEG_HOT_NODE);
|
||||
memcpy(child.p_name, node_blk->i.i_name,
|
||||
node_blk->i.i_namelen);
|
||||
namelen = le32_to_cpu(node_blk->i.i_namelen);
|
||||
if (namelen > F2FS_NAME_LEN)
|
||||
namelen = F2FS_NAME_LEN;
|
||||
memcpy(child.p_name, node_blk->i.i_name, namelen);
|
||||
} else {
|
||||
if (f2fs_test_main_bitmap(sbi, ni->blk_addr) == 0) {
|
||||
f2fs_set_main_bitmap(sbi, ni->blk_addr,
|
||||
@ -926,17 +932,16 @@ void fsck_chk_inode_blk(struct f2fs_sb_info *sbi, u32 nid,
|
||||
}
|
||||
|
||||
/* check data blocks in inode */
|
||||
addrs = ADDRS_PER_INODE(&node_blk->i);
|
||||
if (cur_qtype != -1) {
|
||||
u64 addrs_per_blk = (u64)ADDRS_PER_BLOCK(&node_blk->i);
|
||||
qf_szchk_type[cur_qtype] = QF_SZCHK_REGFILE;
|
||||
qf_maxsize[cur_qtype] = (ADDRS_PER_INODE(&node_blk->i) +
|
||||
2 * ADDRS_PER_BLOCK(&node_blk->i) +
|
||||
2 * ADDRS_PER_BLOCK(&node_blk->i) *
|
||||
NIDS_PER_BLOCK +
|
||||
(u64) ADDRS_PER_BLOCK(&node_blk->i) *
|
||||
NIDS_PER_BLOCK * NIDS_PER_BLOCK) * F2FS_BLKSIZE;
|
||||
qf_maxsize[cur_qtype] = (u64)(addrs + 2 * addrs_per_blk +
|
||||
2 * addrs_per_blk * NIDS_PER_BLOCK +
|
||||
addrs_per_blk * NIDS_PER_BLOCK *
|
||||
NIDS_PER_BLOCK) * F2FS_BLKSIZE;
|
||||
}
|
||||
for (idx = 0; idx < ADDRS_PER_INODE(&node_blk->i);
|
||||
idx++, child.pgofs++) {
|
||||
for (idx = 0; idx < addrs; idx++, child.pgofs++) {
|
||||
block_t blkaddr = le32_to_cpu(node_blk->i.i_addr[ofs + idx]);
|
||||
|
||||
/* check extent info */
|
||||
@ -2059,7 +2064,7 @@ int fsck_chk_meta(struct f2fs_sb_info *sbi)
|
||||
unsigned int i;
|
||||
|
||||
/* 1. check sit usage with CP: curseg is lost? */
|
||||
for (i = 0; i < TOTAL_SEGS(sbi); i++) {
|
||||
for (i = 0; i < MAIN_SEGS(sbi); i++) {
|
||||
se = get_seg_entry(sbi, i);
|
||||
if (se->valid_blocks != 0)
|
||||
sit_valid_segs++;
|
||||
@ -2457,6 +2462,9 @@ static int check_curseg_write_pointer(struct f2fs_sb_info *sbi, int type)
|
||||
if (i >= MAX_DEVICES)
|
||||
return -EINVAL;
|
||||
|
||||
if (c.devices[i].zoned_model != F2FS_ZONED_HM)
|
||||
return 0;
|
||||
|
||||
/* get write pointer position of the zone the curseg points to */
|
||||
cs_sector = (cs_block - c.devices[i].start_blkaddr)
|
||||
<< log_sectors_per_block;
|
||||
@ -2602,7 +2610,7 @@ int check_sit_types(struct f2fs_sb_info *sbi)
|
||||
unsigned int i;
|
||||
int err = 0;
|
||||
|
||||
for (i = 0; i < TOTAL_SEGS(sbi); i++) {
|
||||
for (i = 0; i < MAIN_SEGS(sbi); i++) {
|
||||
struct seg_entry *se;
|
||||
|
||||
se = get_seg_entry(sbi, i);
|
||||
@ -3175,6 +3183,7 @@ int fsck_verify(struct f2fs_sb_info *sbi)
|
||||
struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
|
||||
struct hard_link_node *node = NULL;
|
||||
bool verify_failed = false;
|
||||
uint64_t max_blks, data_secs, node_secs, free_blks;
|
||||
|
||||
if (c.show_file_map)
|
||||
return 0;
|
||||
@ -3225,10 +3234,16 @@ int fsck_verify(struct f2fs_sb_info *sbi)
|
||||
}
|
||||
c.bug_on = 1;
|
||||
}
|
||||
printf("[FSCK] Max image size: %"PRIu64" MB, Free space: %u MB\n",
|
||||
c.max_size >> 20,
|
||||
(sbi->user_block_count - sbi->total_valid_block_count) >>
|
||||
(20 - F2FS_BLKSIZE_BITS));
|
||||
|
||||
data_secs = round_up(sbi->total_valid_node_count, BLKS_PER_SEC(sbi));
|
||||
node_secs = round_up(sbi->total_valid_block_count -
|
||||
sbi->total_valid_node_count, BLKS_PER_SEC(sbi));
|
||||
free_blks = (sbi->total_sections - data_secs - node_secs) *
|
||||
BLKS_PER_SEC(sbi);
|
||||
max_blks = SM_I(sbi)->main_blkaddr + (data_secs + node_secs) *
|
||||
BLKS_PER_SEC(sbi);
|
||||
printf("[FSCK] Max image size: %"PRIu64" MB, Free space: %"PRIu64" MB\n",
|
||||
max_blks >> 8, free_blks >> 8);
|
||||
printf("[FSCK] Unreachable nat entries ");
|
||||
if (nr_unref_nid == 0x0) {
|
||||
printf(" [Ok..] [0x%x]\n", nr_unref_nid);
|
||||
@ -3340,6 +3355,7 @@ int fsck_verify(struct f2fs_sb_info *sbi)
|
||||
/* fix global metadata */
|
||||
if (force || (c.fix_on && f2fs_dev_is_writable())) {
|
||||
struct f2fs_checkpoint *cp = F2FS_CKPT(sbi);
|
||||
struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
|
||||
|
||||
if (force || c.bug_on || c.bug_nat_bits || c.quota_fixed) {
|
||||
/* flush nats to write_nit_bits below */
|
||||
@ -3355,6 +3371,16 @@ int fsck_verify(struct f2fs_sb_info *sbi)
|
||||
is_set_ckpt_flags(cp, CP_QUOTA_NEED_FSCK_FLAG)) {
|
||||
write_checkpoints(sbi);
|
||||
}
|
||||
|
||||
if (c.abnormal_stop)
|
||||
memset(sb->s_stop_reason, 0, MAX_STOP_REASON);
|
||||
|
||||
if (c.fs_errors)
|
||||
memset(sb->s_errors, 0, MAX_F2FS_ERRORS);
|
||||
|
||||
if (c.abnormal_stop || c.fs_errors)
|
||||
update_superblock(sb, SB_MASK_ALL);
|
||||
|
||||
/* to return FSCK_ERROR_CORRECTED */
|
||||
ret = 0;
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ enum SB_ADDR {
|
||||
SB_MAX_ADDR,
|
||||
};
|
||||
|
||||
#define SB_MASK(i) (1 << i)
|
||||
#define SB_MASK(i) (1 << (i))
|
||||
#define SB_MASK_ALL (SB_MASK(SB0_ADDR) | SB_MASK(SB1_ADDR))
|
||||
|
||||
/* fsck.c */
|
||||
@ -237,6 +237,8 @@ extern void update_data_blkaddr(struct f2fs_sb_info *, nid_t, u16, block_t);
|
||||
extern void update_nat_blkaddr(struct f2fs_sb_info *, nid_t, nid_t, block_t);
|
||||
|
||||
extern void print_raw_sb_info(struct f2fs_super_block *);
|
||||
extern bool is_checkpoint_stop(struct f2fs_super_block *, bool);
|
||||
extern bool is_inconsistent_error(struct f2fs_super_block *);
|
||||
extern pgoff_t current_nat_addr(struct f2fs_sb_info *, nid_t, int *);
|
||||
|
||||
extern u32 get_free_segments(struct f2fs_sb_info *);
|
||||
@ -262,6 +264,7 @@ struct dump_option {
|
||||
int start_ssa;
|
||||
int end_ssa;
|
||||
int32_t blk_addr;
|
||||
nid_t scan_nid;
|
||||
};
|
||||
|
||||
extern void nat_dump(struct f2fs_sb_info *, nid_t, nid_t);
|
||||
@ -270,6 +273,7 @@ extern void ssa_dump(struct f2fs_sb_info *, int, int);
|
||||
extern int dump_node(struct f2fs_sb_info *, nid_t, int);
|
||||
extern int dump_info_from_blkaddr(struct f2fs_sb_info *, u32);
|
||||
extern unsigned int start_bidx_of_node(unsigned int, struct f2fs_node *);
|
||||
extern void dump_node_scan_disk(struct f2fs_sb_info *sbi, nid_t nid);
|
||||
|
||||
|
||||
/* defrag.c */
|
||||
|
33
fsck/main.c
33
fsck/main.c
@ -90,6 +90,7 @@ void dump_usage()
|
||||
MSG(0, "[options]:\n");
|
||||
MSG(0, " -d debug level [default:0]\n");
|
||||
MSG(0, " -i inode no (hex)\n");
|
||||
MSG(0, " -I inode no (hex) scan full disk\n");
|
||||
MSG(0, " -n [NAT dump nid from #1~#2 (decimal), for all 0~-1]\n");
|
||||
MSG(0, " -M show a block map\n");
|
||||
MSG(0, " -s [SIT dump segno from #1~#2 (decimal), for all 0~-1]\n");
|
||||
@ -121,7 +122,8 @@ void resize_usage()
|
||||
MSG(0, "[options]:\n");
|
||||
MSG(0, " -d debug level [default:0]\n");
|
||||
MSG(0, " -i extended node bitmap, node ratio is 20%% by default\n");
|
||||
MSG(0, " -s safe resize (Does not resize metadata)");
|
||||
MSG(0, " -o overprovision percentage [default:auto]\n");
|
||||
MSG(0, " -s safe resize (Does not resize metadata)\n");
|
||||
MSG(0, " -t target sectors [default: device size]\n");
|
||||
MSG(0, " -O feature1[,feature2,...] e.g. \"fsprojquota,fscasefold\"\n");
|
||||
MSG(0, " -C [encoding[:flag1,...]] Support casefolding with optional flags\n");
|
||||
@ -273,8 +275,10 @@ void f2fs_parse_options(int argc, char *argv[])
|
||||
atoi(optarg);
|
||||
break;
|
||||
case 'g':
|
||||
if (!strcmp(optarg, "android"))
|
||||
if (!strcmp(optarg, "android")) {
|
||||
c.defset = CONF_ANDROID;
|
||||
MSG(0, "Info: Set conf for android\n");
|
||||
}
|
||||
break;
|
||||
case 'l':
|
||||
c.layout = 1;
|
||||
@ -382,7 +386,7 @@ void f2fs_parse_options(int argc, char *argv[])
|
||||
}
|
||||
} else if (!strcmp("dump.f2fs", prog)) {
|
||||
#ifdef WITH_DUMP
|
||||
const char *option_string = "d:i:n:Ms:Sa:b:V";
|
||||
const char *option_string = "d:i:I:n:Ms:Sa:b:V";
|
||||
static struct dump_option dump_opt = {
|
||||
.nid = 0, /* default root ino */
|
||||
.start_nat = -1,
|
||||
@ -392,6 +396,7 @@ void f2fs_parse_options(int argc, char *argv[])
|
||||
.start_ssa = -1,
|
||||
.end_ssa = -1,
|
||||
.blk_addr = -1,
|
||||
.scan_nid = 0,
|
||||
};
|
||||
|
||||
c.func = DUMP;
|
||||
@ -408,14 +413,6 @@ void f2fs_parse_options(int argc, char *argv[])
|
||||
MSG(0, "Info: Debug level = %d\n",
|
||||
c.dbg_lv);
|
||||
break;
|
||||
case 'g':
|
||||
if (!strcmp(optarg, "android")) {
|
||||
c.defset = CONF_ANDROID;
|
||||
MSG(0, "Info: Set conf for android\n");
|
||||
break;
|
||||
}
|
||||
err = EWRONG_OPT;
|
||||
break;
|
||||
case 'i':
|
||||
if (strncmp(optarg, "0x", 2))
|
||||
ret = sscanf(optarg, "%d",
|
||||
@ -424,6 +421,14 @@ void f2fs_parse_options(int argc, char *argv[])
|
||||
ret = sscanf(optarg, "%x",
|
||||
&dump_opt.nid);
|
||||
break;
|
||||
case 'I':
|
||||
if (strncmp(optarg, "0x", 2))
|
||||
ret = sscanf(optarg, "%d",
|
||||
&dump_opt.scan_nid);
|
||||
else
|
||||
ret = sscanf(optarg, "%x",
|
||||
&dump_opt.scan_nid);
|
||||
break;
|
||||
case 'n':
|
||||
ret = sscanf(optarg, "%d~%d",
|
||||
&dump_opt.start_nat,
|
||||
@ -529,7 +534,7 @@ void f2fs_parse_options(int argc, char *argv[])
|
||||
#endif
|
||||
} else if (!strcmp("resize.f2fs", prog)) {
|
||||
#ifdef WITH_RESIZE
|
||||
const char *option_string = "d:fst:O:C:iV";
|
||||
const char *option_string = "d:fst:O:C:io:V";
|
||||
int val;
|
||||
char *token;
|
||||
|
||||
@ -582,6 +587,8 @@ void f2fs_parse_options(int argc, char *argv[])
|
||||
MSG(0, "\tError: Unknown flag %s\n",token);
|
||||
}
|
||||
c.feature |= cpu_to_le32(F2FS_FEATURE_CASEFOLD);
|
||||
case 'o':
|
||||
c.new_overprovision = atof(optarg);
|
||||
break;
|
||||
case 'V':
|
||||
show_version(prog);
|
||||
@ -936,6 +943,8 @@ static void do_dump(struct f2fs_sb_info *sbi)
|
||||
dump_info_from_blkaddr(sbi, opt->blk_addr);
|
||||
if (opt->nid)
|
||||
dump_node(sbi, opt->nid, 0);
|
||||
if (opt->scan_nid)
|
||||
dump_node_scan_disk(sbi, opt->scan_nid);
|
||||
|
||||
print_cp_state(flag);
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
* Aditya Kali <adityakali@google.com>
|
||||
* Hyojun Kim <hyojun@google.com> - Ported to f2fs-tools
|
||||
*/
|
||||
#include "config.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
252
fsck/mount.c
252
fsck/mount.c
@ -81,7 +81,7 @@ unsigned int get_usable_seg_count(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
unsigned int i, usable_seg_count = 0;
|
||||
|
||||
for (i = 0; i < TOTAL_SEGS(sbi); i++)
|
||||
for (i = 0; i < MAIN_SEGS(sbi); i++)
|
||||
if (is_usable_seg(sbi, i))
|
||||
usable_seg_count++;
|
||||
|
||||
@ -97,7 +97,7 @@ bool is_usable_seg(struct f2fs_sb_info *UNUSED(sbi), unsigned int UNUSED(segno))
|
||||
|
||||
unsigned int get_usable_seg_count(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
return TOTAL_SEGS(sbi);
|
||||
return MAIN_SEGS(sbi);
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -106,7 +106,7 @@ u32 get_free_segments(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
u32 i, free_segs = 0;
|
||||
|
||||
for (i = 0; i < TOTAL_SEGS(sbi); i++) {
|
||||
for (i = 0; i < MAIN_SEGS(sbi); i++) {
|
||||
struct seg_entry *se = get_seg_entry(sbi, i);
|
||||
|
||||
if (se->valid_blocks == 0x0 && !IS_CUR_SEGNO(sbi, i) &&
|
||||
@ -584,6 +584,70 @@ void print_sb_state(struct f2fs_super_block *sb)
|
||||
MSG(0, "\n");
|
||||
}
|
||||
|
||||
static char *stop_reason_str[] = {
|
||||
[STOP_CP_REASON_SHUTDOWN] = "shutdown",
|
||||
[STOP_CP_REASON_FAULT_INJECT] = "fault_inject",
|
||||
[STOP_CP_REASON_META_PAGE] = "meta_page",
|
||||
[STOP_CP_REASON_WRITE_FAIL] = "write_fail",
|
||||
[STOP_CP_REASON_CORRUPTED_SUMMARY] = "corrupted_summary",
|
||||
[STOP_CP_REASON_UPDATE_INODE] = "update_inode",
|
||||
[STOP_CP_REASON_FLUSH_FAIL] = "flush_fail",
|
||||
};
|
||||
|
||||
void print_sb_stop_reason(struct f2fs_super_block *sb)
|
||||
{
|
||||
u8 *reason = sb->s_stop_reason;
|
||||
int i;
|
||||
|
||||
if (!c.force_stop)
|
||||
return;
|
||||
|
||||
MSG(0, "Info: checkpoint stop reason: ");
|
||||
|
||||
for (i = 0; i < STOP_CP_REASON_MAX; i++) {
|
||||
if (reason[i])
|
||||
MSG(0, "%s(%d) ", stop_reason_str[i], reason[i]);
|
||||
}
|
||||
|
||||
MSG(0, "\n");
|
||||
}
|
||||
|
||||
static char *errors_str[] = {
|
||||
[ERROR_CORRUPTED_CLUSTER] = "corrupted_cluster",
|
||||
[ERROR_FAIL_DECOMPRESSION] = "fail_decompression",
|
||||
[ERROR_INVALID_BLKADDR] = "invalid_blkaddr",
|
||||
[ERROR_CORRUPTED_DIRENT] = "corrupted_dirent",
|
||||
[ERROR_CORRUPTED_INODE] = "corrupted_inode",
|
||||
[ERROR_INCONSISTENT_SUMMARY] = "inconsistent_summary",
|
||||
[ERROR_INCONSISTENT_FOOTER] = "inconsistent_footer",
|
||||
[ERROR_INCONSISTENT_SUM_TYPE] = "inconsistent_sum_type",
|
||||
[ERROR_CORRUPTED_JOURNAL] = "corrupted_journal",
|
||||
[ERROR_INCONSISTENT_NODE_COUNT] = "inconsistent_node_count",
|
||||
[ERROR_INCONSISTENT_BLOCK_COUNT] = "inconsistent_block_count",
|
||||
[ERROR_INVALID_CURSEG] = "invalid_curseg",
|
||||
[ERROR_INCONSISTENT_SIT] = "inconsistent_sit",
|
||||
[ERROR_CORRUPTED_VERITY_XATTR] = "corrupted_verity_xattr",
|
||||
[ERROR_CORRUPTED_XATTR] = "corrupted_xattr",
|
||||
};
|
||||
|
||||
void print_sb_errors(struct f2fs_super_block *sb)
|
||||
{
|
||||
u8 *errors = sb->s_errors;
|
||||
int i;
|
||||
|
||||
if (!c.fs_errors)
|
||||
return;
|
||||
|
||||
MSG(0, "Info: fs errors: ");
|
||||
|
||||
for (i = 0; i < ERROR_MAX; i++) {
|
||||
if (test_bit_le(i, errors))
|
||||
MSG(0, "%s ", errors_str[i]);
|
||||
}
|
||||
|
||||
MSG(0, "\n");
|
||||
}
|
||||
|
||||
bool f2fs_is_valid_blkaddr(struct f2fs_sb_info *sbi,
|
||||
block_t blkaddr, int type)
|
||||
{
|
||||
@ -803,21 +867,15 @@ int sanity_check_raw_super(struct f2fs_super_block *sb, enum SB_ADDR sb_addr)
|
||||
unsigned int segment_count, segs_per_sec, secs_per_zone, segs_per_zone;
|
||||
unsigned int total_sections, blocks_per_seg;
|
||||
|
||||
if ((get_sb(feature) & F2FS_FEATURE_SB_CHKSUM) &&
|
||||
verify_sb_chksum(sb))
|
||||
return -1;
|
||||
|
||||
if (F2FS_SUPER_MAGIC != get_sb(magic)) {
|
||||
MSG(0, "Magic Mismatch, valid(0x%x) - read(0x%x)\n",
|
||||
F2FS_SUPER_MAGIC, get_sb(magic));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (F2FS_BLKSIZE != PAGE_CACHE_SIZE) {
|
||||
MSG(0, "Invalid page_cache_size (%d), supports only 4KB\n",
|
||||
PAGE_CACHE_SIZE);
|
||||
if ((get_sb(feature) & F2FS_FEATURE_SB_CHKSUM) &&
|
||||
verify_sb_chksum(sb))
|
||||
return -1;
|
||||
}
|
||||
|
||||
blocksize = 1 << get_sb(log_blocksize);
|
||||
if (F2FS_BLKSIZE != blocksize) {
|
||||
@ -930,7 +988,7 @@ int sanity_check_raw_super(struct f2fs_super_block *sb, enum SB_ADDR sb_addr)
|
||||
}
|
||||
|
||||
/* Check zoned block device feature */
|
||||
if (c.devices[0].zoned_model == F2FS_ZONED_HM &&
|
||||
if (c.devices[0].zoned_model != F2FS_ZONED_NONE &&
|
||||
!(sb->feature & cpu_to_le32(F2FS_FEATURE_BLKZONED))) {
|
||||
MSG(0, "\tMissing zoned block device feature\n");
|
||||
return -1;
|
||||
@ -1012,53 +1070,16 @@ int validate_super_block(struct f2fs_sb_info *sbi, enum SB_ADDR sb_addr)
|
||||
VERSION_NAME_LEN);
|
||||
get_kernel_version(c.init_version);
|
||||
|
||||
c.force_stop = is_checkpoint_stop(sbi->raw_super, false);
|
||||
c.abnormal_stop = is_checkpoint_stop(sbi->raw_super, true);
|
||||
c.fs_errors = is_inconsistent_error(sbi->raw_super);
|
||||
|
||||
MSG(0, "Info: MKFS version\n \"%s\"\n", c.init_version);
|
||||
MSG(0, "Info: FSCK version\n from \"%s\"\n to \"%s\"\n",
|
||||
c.sb_version, c.version);
|
||||
#if defined(__APPLE__)
|
||||
if (!c.no_kernel_check &&
|
||||
memcmp(c.sb_version, c.version, VERSION_NAME_LEN)) {
|
||||
c.auto_fix = 0;
|
||||
c.fix_on = 1;
|
||||
memcpy(sbi->raw_super->version,
|
||||
c.version, VERSION_NAME_LEN);
|
||||
update_superblock(sbi->raw_super, SB_MASK(sb_addr));
|
||||
}
|
||||
#else
|
||||
if (!c.no_kernel_check) {
|
||||
struct timespec t;
|
||||
u32 prev_time, cur_time, time_diff;
|
||||
__le32 *ver_ts_ptr = (__le32 *)(sbi->raw_super->version
|
||||
+ VERSION_NAME_LEN);
|
||||
|
||||
t.tv_sec = t.tv_nsec = 0;
|
||||
clock_gettime(CLOCK_REALTIME, &t);
|
||||
cur_time = (u32)t.tv_sec;
|
||||
prev_time = le32_to_cpu(*ver_ts_ptr);
|
||||
|
||||
MSG(0, "Info: version timestamp cur: %u, prev: %u\n",
|
||||
cur_time, prev_time);
|
||||
if (!memcmp(c.sb_version, c.version,
|
||||
VERSION_NAME_LEN)) {
|
||||
/* valid prev_time */
|
||||
if (prev_time != 0 && cur_time > prev_time) {
|
||||
time_diff = cur_time - prev_time;
|
||||
if (time_diff < CHECK_PERIOD)
|
||||
goto out;
|
||||
c.auto_fix = 0;
|
||||
c.fix_on = 1;
|
||||
}
|
||||
} else {
|
||||
memcpy(sbi->raw_super->version,
|
||||
c.version, VERSION_NAME_LEN);
|
||||
}
|
||||
|
||||
*ver_ts_ptr = cpu_to_le32(cur_time);
|
||||
update_superblock(sbi->raw_super, SB_MASK(sb_addr));
|
||||
}
|
||||
out:
|
||||
#endif
|
||||
print_sb_state(sbi->raw_super);
|
||||
print_sb_stop_reason(sbi->raw_super);
|
||||
print_sb_errors(sbi->raw_super);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1114,6 +1135,14 @@ int init_sb_info(struct f2fs_sb_info *sbi)
|
||||
if (i == 0)
|
||||
c.devices[i].end_blkaddr += get_sb(segment0_blkaddr);
|
||||
|
||||
if (c.zoned_model == F2FS_ZONED_NONE) {
|
||||
if (c.devices[i].zoned_model == F2FS_ZONED_HM)
|
||||
c.zoned_model = F2FS_ZONED_HM;
|
||||
else if (c.devices[i].zoned_model == F2FS_ZONED_HA &&
|
||||
c.zoned_model != F2FS_ZONED_HM)
|
||||
c.zoned_model = F2FS_ZONED_HA;
|
||||
}
|
||||
|
||||
c.ndevs = i + 1;
|
||||
MSG(0, "Info: Device[%d] : %s blkaddr = %"PRIx64"--%"PRIx64"\n",
|
||||
i, c.devices[i].path,
|
||||
@ -1155,7 +1184,7 @@ static void *get_checkpoint_version(block_t cp_addr)
|
||||
{
|
||||
void *cp_page;
|
||||
|
||||
cp_page = malloc(PAGE_SIZE);
|
||||
cp_page = malloc(F2FS_BLKSIZE);
|
||||
ASSERT(cp_page);
|
||||
|
||||
if (dev_read_block(cp_page, cp_addr) < 0)
|
||||
@ -1219,7 +1248,7 @@ int get_valid_checkpoint(struct f2fs_sb_info *sbi)
|
||||
int ret;
|
||||
|
||||
cp_payload = get_sb(cp_payload);
|
||||
if (cp_payload > F2FS_BLK_ALIGN(MAX_SIT_BITMAP_SIZE))
|
||||
if (cp_payload > F2FS_BLK_ALIGN(MAX_CP_PAYLOAD))
|
||||
return -EINVAL;
|
||||
|
||||
cp_blks = 1 + cp_payload;
|
||||
@ -1290,6 +1319,32 @@ fail_no_cp:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
bool is_checkpoint_stop(struct f2fs_super_block *sb, bool abnormal)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < STOP_CP_REASON_MAX; i++) {
|
||||
if (abnormal && i == STOP_CP_REASON_SHUTDOWN)
|
||||
continue;
|
||||
if (sb->s_stop_reason[i])
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_inconsistent_error(struct f2fs_super_block *sb)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_F2FS_ERRORS; i++) {
|
||||
if (sb->s_errors[i])
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* For a return value of 1, caller should further check for c.fix_on state
|
||||
* and take appropriate action.
|
||||
@ -1299,6 +1354,7 @@ static int f2fs_should_proceed(struct f2fs_super_block *sb, u32 flag)
|
||||
if (!c.fix_on && (c.auto_fix || c.preen_mode)) {
|
||||
if (flag & CP_FSCK_FLAG ||
|
||||
flag & CP_QUOTA_NEED_FSCK_FLAG ||
|
||||
c.abnormal_stop || c.fs_errors ||
|
||||
(exist_qf_ino(sb) && (flag & CP_ERROR_FLAG))) {
|
||||
c.fix_on = 1;
|
||||
} else if (!c.preen_mode) {
|
||||
@ -1773,13 +1829,13 @@ int build_sit_info(struct f2fs_sb_info *sbi)
|
||||
|
||||
SM_I(sbi)->sit_info = sit_i;
|
||||
|
||||
sit_i->sentries = calloc(TOTAL_SEGS(sbi) * sizeof(struct seg_entry), 1);
|
||||
sit_i->sentries = calloc(MAIN_SEGS(sbi) * sizeof(struct seg_entry), 1);
|
||||
if (!sit_i->sentries) {
|
||||
MSG(1, "\tError: Calloc failed for build_sit_info!\n");
|
||||
goto free_sit_info;
|
||||
}
|
||||
|
||||
bitmap_size = TOTAL_SEGS(sbi) * SIT_VBLOCK_MAP_SIZE;
|
||||
bitmap_size = MAIN_SEGS(sbi) * SIT_VBLOCK_MAP_SIZE;
|
||||
|
||||
if (need_fsync_data_record(sbi))
|
||||
bitmap_size += bitmap_size;
|
||||
@ -1792,7 +1848,7 @@ int build_sit_info(struct f2fs_sb_info *sbi)
|
||||
|
||||
bitmap = sit_i->bitmap;
|
||||
|
||||
for (start = 0; start < TOTAL_SEGS(sbi); start++) {
|
||||
for (start = 0; start < MAIN_SEGS(sbi); start++) {
|
||||
sit_i->sentries[start].cur_valid_map = bitmap;
|
||||
bitmap += SIT_VBLOCK_MAP_SIZE;
|
||||
|
||||
@ -1861,7 +1917,7 @@ static void read_compacted_summaries(struct f2fs_sb_info *sbi)
|
||||
|
||||
start = start_sum_block(sbi);
|
||||
|
||||
kaddr = (char *)malloc(PAGE_SIZE);
|
||||
kaddr = malloc(F2FS_BLKSIZE);
|
||||
ASSERT(kaddr);
|
||||
|
||||
ret = dev_read_block(kaddr, start++);
|
||||
@ -1894,9 +1950,9 @@ static void read_compacted_summaries(struct f2fs_sb_info *sbi)
|
||||
curseg->sum_blk->entries[j] = *s;
|
||||
offset += SUMMARY_SIZE;
|
||||
if (offset + SUMMARY_SIZE <=
|
||||
PAGE_CACHE_SIZE - SUM_FOOTER_SIZE)
|
||||
F2FS_BLKSIZE - SUM_FOOTER_SIZE)
|
||||
continue;
|
||||
memset(kaddr, 0, PAGE_SIZE);
|
||||
memset(kaddr, 0, F2FS_BLKSIZE);
|
||||
ret = dev_read_block(kaddr, start++);
|
||||
ASSERT(ret >= 0);
|
||||
offset = 0;
|
||||
@ -1954,7 +2010,7 @@ static void read_normal_summaries(struct f2fs_sb_info *sbi, int type)
|
||||
blk_addr = GET_SUM_BLKADDR(sbi, segno);
|
||||
}
|
||||
|
||||
sum_blk = (struct f2fs_summary_block *)malloc(PAGE_SIZE);
|
||||
sum_blk = malloc(sizeof(*sum_blk));
|
||||
ASSERT(sum_blk);
|
||||
|
||||
ret = dev_read_block(sum_blk, blk_addr);
|
||||
@ -1964,7 +2020,7 @@ static void read_normal_summaries(struct f2fs_sb_info *sbi, int type)
|
||||
restore_node_summary(sbi, segno, sum_blk);
|
||||
|
||||
curseg = CURSEG_I(sbi, type);
|
||||
memcpy(curseg->sum_blk, sum_blk, PAGE_CACHE_SIZE);
|
||||
memcpy(curseg->sum_blk, sum_blk, sizeof(*sum_blk));
|
||||
reset_curseg(sbi, type);
|
||||
free(sum_blk);
|
||||
}
|
||||
@ -2030,7 +2086,7 @@ static int build_curseg(struct f2fs_sb_info *sbi)
|
||||
SM_I(sbi)->curseg_array = array;
|
||||
|
||||
for (i = 0; i < NR_CURSEG_TYPE; i++) {
|
||||
array[i].sum_blk = calloc(PAGE_CACHE_SIZE, 1);
|
||||
array[i].sum_blk = calloc(sizeof(*(array[i].sum_blk)), 1);
|
||||
if (!array[i].sum_blk) {
|
||||
MSG(1, "\tError: Calloc failed for build_curseg!!\n");
|
||||
goto seg_cleanup;
|
||||
@ -2044,7 +2100,7 @@ static int build_curseg(struct f2fs_sb_info *sbi)
|
||||
blk_off = get_cp(cur_node_blkoff[i - CURSEG_HOT_NODE]);
|
||||
segno = get_cp(cur_node_segno[i - CURSEG_HOT_NODE]);
|
||||
}
|
||||
ASSERT(segno < TOTAL_SEGS(sbi));
|
||||
ASSERT(segno < MAIN_SEGS(sbi));
|
||||
ASSERT(blk_off < DEFAULT_BLOCKS_PER_SEGMENT);
|
||||
|
||||
array[i].segno = segno;
|
||||
@ -2406,7 +2462,7 @@ static int build_sit_entries(struct f2fs_sb_info *sbi)
|
||||
segno = start_blk * sit_i->sents_per_block;
|
||||
end = (start_blk + readed) * sit_i->sents_per_block;
|
||||
|
||||
for (; segno < end && segno < TOTAL_SEGS(sbi); segno++) {
|
||||
for (; segno < end && segno < MAIN_SEGS(sbi); segno++) {
|
||||
se = &sit_i->sentries[segno];
|
||||
|
||||
get_current_sit_page(sbi, segno, sit_blk);
|
||||
@ -2432,7 +2488,7 @@ static int build_sit_entries(struct f2fs_sb_info *sbi)
|
||||
for (i = 0; i < sits_in_cursum(journal); i++) {
|
||||
segno = le32_to_cpu(segno_in_journal(journal, i));
|
||||
|
||||
if (segno >= TOTAL_SEGS(sbi)) {
|
||||
if (segno >= MAIN_SEGS(sbi)) {
|
||||
MSG(0, "\tError: build_sit_entries: segno(%u) is invalid!!!\n", segno);
|
||||
journal->n_sits = cpu_to_le16(i);
|
||||
c.fix_on = 1;
|
||||
@ -2509,7 +2565,7 @@ void build_sit_area_bitmap(struct f2fs_sb_info *sbi)
|
||||
|
||||
ASSERT(fsck->sit_area_bitmap_sz == fsck->main_area_bitmap_sz);
|
||||
|
||||
for (segno = 0; segno < TOTAL_SEGS(sbi); segno++) {
|
||||
for (segno = 0; segno < MAIN_SEGS(sbi); segno++) {
|
||||
se = get_seg_entry(sbi, segno);
|
||||
|
||||
memcpy(ptr, se->cur_valid_map, SIT_VBLOCK_MAP_SIZE);
|
||||
@ -2555,7 +2611,7 @@ void rewrite_sit_area_bitmap(struct f2fs_sb_info *sbi)
|
||||
|
||||
ptr = fsck->main_area_bitmap;
|
||||
|
||||
for (segno = 0; segno < TOTAL_SEGS(sbi); segno++) {
|
||||
for (segno = 0; segno < MAIN_SEGS(sbi); segno++) {
|
||||
struct f2fs_sit_entry *sit;
|
||||
struct seg_entry *se;
|
||||
u16 valid_blocks = 0;
|
||||
@ -2678,7 +2734,7 @@ void flush_sit_entries(struct f2fs_sb_info *sbi)
|
||||
sit_blk = calloc(BLOCK_SZ, 1);
|
||||
ASSERT(sit_blk);
|
||||
/* update free segments */
|
||||
for (segno = 0; segno < TOTAL_SEGS(sbi); segno++) {
|
||||
for (segno = 0; segno < MAIN_SEGS(sbi); segno++) {
|
||||
struct f2fs_sit_entry *sit;
|
||||
struct seg_entry *se;
|
||||
|
||||
@ -2866,6 +2922,14 @@ static void move_one_curseg_info(struct f2fs_sb_info *sbi, u64 from, int left,
|
||||
if ((get_sb(feature) & cpu_to_le32(F2FS_FEATURE_RO))) {
|
||||
if (i != CURSEG_HOT_DATA && i != CURSEG_HOT_NODE)
|
||||
return;
|
||||
|
||||
if (i == CURSEG_HOT_DATA) {
|
||||
left = 0;
|
||||
from = SM_I(sbi)->main_blkaddr;
|
||||
} else {
|
||||
left = 1;
|
||||
from = __end_block_addr(sbi);
|
||||
}
|
||||
goto bypass_ssa;
|
||||
}
|
||||
|
||||
@ -3376,7 +3440,7 @@ static int find_fsync_inode(struct f2fs_sb_info *sbi, struct list_head *head)
|
||||
struct f2fs_node *node_blk;
|
||||
block_t blkaddr;
|
||||
unsigned int loop_cnt = 0;
|
||||
unsigned int free_blocks = TOTAL_SEGS(sbi) * sbi->blocks_per_seg -
|
||||
unsigned int free_blocks = MAIN_SEGS(sbi) * sbi->blocks_per_seg -
|
||||
sbi->total_valid_block_count;
|
||||
int err = 0;
|
||||
|
||||
@ -3634,6 +3698,48 @@ int f2fs_do_mount(struct f2fs_sb_info *sbi)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (c.func == FSCK) {
|
||||
#if defined(__APPLE__)
|
||||
if (!c.no_kernel_check &&
|
||||
memcmp(c.sb_version, c.version, VERSION_NAME_LEN)) {
|
||||
c.auto_fix = 0;
|
||||
c.fix_on = 1;
|
||||
memcpy(sbi->raw_super->version,
|
||||
c.version, VERSION_NAME_LEN);
|
||||
update_superblock(sbi->raw_super, SB_MASK_ALL);
|
||||
}
|
||||
#else
|
||||
if (!c.no_kernel_check) {
|
||||
u32 prev_time, cur_time, time_diff;
|
||||
__le32 *ver_ts_ptr = (__le32 *)(sbi->raw_super->version
|
||||
+ VERSION_NAME_LEN);
|
||||
|
||||
cur_time = (u32)get_cp(elapsed_time);
|
||||
prev_time = le32_to_cpu(*ver_ts_ptr);
|
||||
|
||||
MSG(0, "Info: version timestamp cur: %u, prev: %u\n",
|
||||
cur_time, prev_time);
|
||||
if (!memcmp(c.sb_version, c.version,
|
||||
VERSION_NAME_LEN)) {
|
||||
/* valid prev_time */
|
||||
if (prev_time != 0 && cur_time > prev_time) {
|
||||
time_diff = cur_time - prev_time;
|
||||
if (time_diff < CHECK_PERIOD)
|
||||
goto out;
|
||||
c.auto_fix = 0;
|
||||
c.fix_on = 1;
|
||||
}
|
||||
} else {
|
||||
memcpy(sbi->raw_super->version,
|
||||
c.version, VERSION_NAME_LEN);
|
||||
}
|
||||
|
||||
*ver_ts_ptr = cpu_to_le32(cur_time);
|
||||
update_superblock(sbi->raw_super, SB_MASK_ALL);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
out:
|
||||
print_ckpt_info(sbi);
|
||||
|
||||
if (c.quota_fix) {
|
||||
|
@ -6,7 +6,6 @@
|
||||
* Hyojun Kim <hyojun@google.com> - Ported to f2fs-tools
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
@ -5,7 +5,6 @@
|
||||
* Hyojun Kim <hyojun@google.com> - Ported to f2fs-tools
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <sys/types.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
|
@ -5,7 +5,6 @@
|
||||
* Hyojun Kim <hyojun@google.com> - Ported to f2fs-tools
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <sys/types.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
|
@ -146,9 +146,11 @@ safe_resize:
|
||||
get_sb(segs_per_sec));
|
||||
|
||||
/* Let's determine the best reserved and overprovisioned space */
|
||||
c.new_overprovision = get_best_overprovision(sb);
|
||||
if (c.new_overprovision == 0)
|
||||
c.new_overprovision = get_best_overprovision(sb);
|
||||
|
||||
c.new_reserved_segments =
|
||||
(2 * (100 / c.new_overprovision + 1) + 6) *
|
||||
(100 / c.new_overprovision + 1 + NR_CURSEG_TYPE) *
|
||||
get_sb(segs_per_sec);
|
||||
|
||||
if ((get_sb(segment_count_main) - 2) < c.new_reserved_segments ||
|
||||
@ -173,7 +175,7 @@ static void migrate_main(struct f2fs_sb_info *sbi, unsigned int offset)
|
||||
|
||||
ASSERT(raw != NULL);
|
||||
|
||||
for (i = TOTAL_SEGS(sbi) - 1; i >= 0; i--) {
|
||||
for (i = MAIN_SEGS(sbi) - 1; i >= 0; i--) {
|
||||
se = get_seg_entry(sbi, i);
|
||||
if (!se->valid_blocks)
|
||||
continue;
|
||||
@ -238,7 +240,7 @@ static void migrate_ssa(struct f2fs_sb_info *sbi,
|
||||
block_t new_sum_blkaddr = get_newsb(ssa_blkaddr);
|
||||
block_t end_sum_blkaddr = get_newsb(main_blkaddr);
|
||||
block_t expand_sum_blkaddr = new_sum_blkaddr +
|
||||
TOTAL_SEGS(sbi) - offset;
|
||||
MAIN_SEGS(sbi) - offset;
|
||||
block_t blkaddr;
|
||||
int ret;
|
||||
void *zero_block = calloc(BLOCK_SZ, 1);
|
||||
@ -256,7 +258,7 @@ static void migrate_ssa(struct f2fs_sb_info *sbi,
|
||||
}
|
||||
} else {
|
||||
blkaddr = end_sum_blkaddr - 1;
|
||||
offset = TOTAL_SEGS(sbi) - 1;
|
||||
offset = MAIN_SEGS(sbi) - 1;
|
||||
while (blkaddr >= new_sum_blkaddr) {
|
||||
if (blkaddr >= expand_sum_blkaddr) {
|
||||
ret = dev_write_block(zero_block, blkaddr--);
|
||||
@ -410,7 +412,7 @@ static void migrate_sit(struct f2fs_sb_info *sbi,
|
||||
DBG(3, "Write zero sit: %x\n", get_newsb(sit_blkaddr) + index);
|
||||
}
|
||||
|
||||
for (segno = 0; segno < TOTAL_SEGS(sbi); segno++) {
|
||||
for (segno = 0; segno < MAIN_SEGS(sbi); segno++) {
|
||||
struct f2fs_sit_entry *sit;
|
||||
|
||||
se = get_seg_entry(sbi, segno);
|
||||
@ -473,8 +475,18 @@ static void rebuild_checkpoint(struct f2fs_sb_info *sbi,
|
||||
set_cp(overprov_segment_count, (get_newsb(segment_count_main) -
|
||||
get_cp(rsvd_segment_count)) *
|
||||
c.new_overprovision / 100);
|
||||
|
||||
/* give 2 sections (DATA and NODE) to trigger GC in advance */
|
||||
if (get_cp(overprov_segment_count) < get_cp(rsvd_segment_count))
|
||||
set_cp(overprov_segment_count, get_cp(rsvd_segment_count));
|
||||
|
||||
set_cp(overprov_segment_count, get_cp(overprov_segment_count) +
|
||||
get_cp(rsvd_segment_count));
|
||||
2 * get_sb(segs_per_sec));
|
||||
|
||||
DBG(0, "Info: Overprovision ratio = %.3lf%%\n", c.new_overprovision);
|
||||
DBG(0, "Info: Overprovision segments = %u (GC reserved = %u)\n",
|
||||
get_cp(overprov_segment_count),
|
||||
c.new_reserved_segments);
|
||||
|
||||
free_segment_count = get_free_segments(sbi);
|
||||
new_segment_count = get_newsb(segment_count_main) -
|
||||
@ -591,6 +603,27 @@ static void rebuild_checkpoint(struct f2fs_sb_info *sbi,
|
||||
DBG(0, "Info: Done to rebuild checkpoint blocks\n");
|
||||
}
|
||||
|
||||
static int f2fs_resize_check(struct f2fs_sb_info *sbi, struct f2fs_super_block *new_sb)
|
||||
{
|
||||
struct f2fs_checkpoint *cp = F2FS_CKPT(sbi);
|
||||
block_t user_block_count;
|
||||
unsigned int overprov_segment_count;
|
||||
|
||||
overprov_segment_count = (get_newsb(segment_count_main) -
|
||||
c.new_reserved_segments) *
|
||||
c.new_overprovision / 100;
|
||||
|
||||
overprov_segment_count += 2 * get_newsb(segs_per_sec);
|
||||
|
||||
user_block_count = (get_newsb(segment_count_main) -
|
||||
overprov_segment_count) * c.blks_per_seg;
|
||||
|
||||
if (get_cp(valid_block_count) > user_block_count)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int f2fs_resize_grow(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
|
||||
@ -608,6 +641,9 @@ static int f2fs_resize_grow(struct f2fs_sb_info *sbi)
|
||||
if (get_new_sb(new_sb))
|
||||
return -1;
|
||||
|
||||
if (f2fs_resize_check(sbi, new_sb) < 0)
|
||||
return -1;
|
||||
|
||||
/* check nat availability */
|
||||
if (get_sb(segment_count_nat) > get_newsb(segment_count_nat)) {
|
||||
err = shrink_nats(sbi, new_sb);
|
||||
@ -651,11 +687,8 @@ static int f2fs_resize_shrink(struct f2fs_sb_info *sbi)
|
||||
struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
|
||||
struct f2fs_super_block new_sb_raw;
|
||||
struct f2fs_super_block *new_sb = &new_sb_raw;
|
||||
struct f2fs_checkpoint *cp = F2FS_CKPT(sbi);
|
||||
block_t old_end_blkaddr, old_main_blkaddr;
|
||||
block_t new_end_blkaddr, new_main_blkaddr, tmp_end_blkaddr;
|
||||
block_t user_block_count;
|
||||
unsigned int overprov_segment_count;
|
||||
unsigned int offset;
|
||||
int err = -1;
|
||||
|
||||
@ -666,15 +699,7 @@ static int f2fs_resize_shrink(struct f2fs_sb_info *sbi)
|
||||
if (get_new_sb(new_sb))
|
||||
return -1;
|
||||
|
||||
overprov_segment_count = (get_newsb(segment_count_main) -
|
||||
c.new_reserved_segments) *
|
||||
c.new_overprovision / 100;
|
||||
overprov_segment_count += c.new_reserved_segments;
|
||||
|
||||
user_block_count = (get_newsb(segment_count_main) -
|
||||
overprov_segment_count) * c.blks_per_seg;
|
||||
|
||||
if (get_cp(valid_block_count) > user_block_count)
|
||||
if (f2fs_resize_check(sbi, new_sb) < 0)
|
||||
return -1;
|
||||
|
||||
/* check nat availability */
|
||||
|
@ -28,6 +28,7 @@ int reserve_new_block(struct f2fs_sb_info *sbi, block_t *to,
|
||||
u64 blkaddr, offset;
|
||||
u64 old_blkaddr = *to;
|
||||
bool is_node = IS_NODESEG(type);
|
||||
int left = 0;
|
||||
|
||||
if (old_blkaddr == NULL_ADDR) {
|
||||
if (c.func == FSCK) {
|
||||
@ -56,7 +57,19 @@ int reserve_new_block(struct f2fs_sb_info *sbi, block_t *to,
|
||||
|
||||
blkaddr = SM_I(sbi)->main_blkaddr;
|
||||
|
||||
if (find_next_free_block(sbi, &blkaddr, 0, type, false)) {
|
||||
if (sbi->raw_super->feature & cpu_to_le32(F2FS_FEATURE_RO)) {
|
||||
if (IS_NODESEG(type)) {
|
||||
type = CURSEG_HOT_NODE;
|
||||
blkaddr = __end_block_addr(sbi);
|
||||
left = 1;
|
||||
} else if (IS_DATASEG(type)) {
|
||||
type = CURSEG_HOT_DATA;
|
||||
blkaddr = SM_I(sbi)->main_blkaddr;
|
||||
left = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (find_next_free_block(sbi, &blkaddr, left, type, false)) {
|
||||
ERR_MSG("Can't find free block");
|
||||
ASSERT(0);
|
||||
}
|
||||
|
@ -320,7 +320,7 @@ static int configure_files(void)
|
||||
#else
|
||||
sehnd = selinux_android_file_context_handle();
|
||||
if (!sehnd) {
|
||||
ERR_MSG("Failed to get android file_contexts\n", c.mount_point);
|
||||
ERR_MSG("Failed to get android file_contexts\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
#endif
|
||||
|
@ -121,7 +121,7 @@ static void write_all_xattrs(struct f2fs_sb_info *sbi,
|
||||
/* write to xattr node block */
|
||||
xattr_addr = (void *)xattr_node;
|
||||
memcpy(xattr_addr, txattr_addr + inline_size,
|
||||
PAGE_SIZE - sizeof(struct node_footer));
|
||||
F2FS_BLKSIZE - sizeof(struct node_footer));
|
||||
|
||||
ret = dev_write_block(xattr_node, blkaddr);
|
||||
|
||||
|
@ -35,7 +35,9 @@ struct f2fs_xattr_entry {
|
||||
};
|
||||
|
||||
#define FS_ENCRYPTION_CONTEXT_FORMAT_V1 1
|
||||
#ifndef FS_KEY_DESCRIPTOR_SIZE
|
||||
#define FS_KEY_DESCRIPTOR_SIZE 8
|
||||
#endif
|
||||
#define FS_KEY_DERIVATION_NONCE_SIZE 16
|
||||
|
||||
struct fscrypt_context {
|
||||
@ -130,7 +132,7 @@ static inline int f2fs_acl_count(int size)
|
||||
!IS_XATTR_LAST_ENTRY(entry); \
|
||||
entry = XATTR_NEXT_ENTRY(entry))
|
||||
|
||||
#define MIN_OFFSET XATTR_ALIGN(PAGE_SIZE - \
|
||||
#define MIN_OFFSET XATTR_ALIGN(F2FS_BLKSIZE - \
|
||||
sizeof(struct node_footer) - sizeof(__u32))
|
||||
|
||||
#define MAX_VALUE_LEN (MIN_OFFSET - \
|
||||
|
@ -4,8 +4,6 @@
|
||||
#define HAVE_FCNTL_H 1
|
||||
#define HAVE_FALLOC_H 1
|
||||
#define HAVE_FSYNC 1
|
||||
#define HAVE_KERNEL_UAPI_LINUX_BLKZONED_H 1
|
||||
#define HAVE_LINUX_BLKZONED_H 1
|
||||
#define HAVE_LINUX_HDREG_H 1
|
||||
#define HAVE_LINUX_LIMITS_H 1
|
||||
#define HAVE_POSIX_ACL_H 1
|
||||
@ -23,7 +21,9 @@
|
||||
#define HAVE_SYS_SYSMACROS_H 1
|
||||
#define HAVE_SYS_XATTR_H 1
|
||||
#define HAVE_UNISTD_H 1
|
||||
#define HAVE_SCSI_SG_H 1
|
||||
#define HAVE_UUID_UUID_H 1
|
||||
#define HAVE_CLOCK_GETTIME 1
|
||||
#define HAVE_CLOCK_BOOTTIME 1
|
||||
|
||||
#define HAVE_FALLOCATE 1
|
||||
#define HAVE_FSETXATTR 1
|
||||
@ -37,10 +37,16 @@
|
||||
#define HAVE_SETMNTENT 1
|
||||
#define HAVE_SPARSE_SPARSE_H 1
|
||||
#define HAVE_LIBLZ4 1
|
||||
#define HAVE_LIBUUID 1
|
||||
|
||||
#ifdef WITH_SLOAD
|
||||
#define HAVE_LIBSELINUX 1
|
||||
#endif
|
||||
|
||||
#if defined(__BIONIC__)
|
||||
#define HAVE_LINUX_BLKZONED_H 1
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
@ -63,6 +69,7 @@
|
||||
#define HAVE_GETMNTENT 1
|
||||
#define HAVE_LLSEEK 1
|
||||
#define HAVE_MEMSET 1
|
||||
#define HAVE_SPARSE_SPARSE_H 1
|
||||
#define HAVE_LIBLZ4 1
|
||||
|
||||
#ifdef WITH_SLOAD
|
||||
@ -72,4 +79,5 @@
|
||||
|
||||
#if defined(_WIN32)
|
||||
#define HAVE_LSEEK64
|
||||
#define HAVE_SPARSE_SPARSE_H 1
|
||||
#endif
|
||||
|
@ -77,11 +77,17 @@
|
||||
#define static_assert _Static_assert
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_MOUNT_H
|
||||
#include <sys/mount.h>
|
||||
#endif
|
||||
|
||||
#ifndef fallthrough
|
||||
#ifdef __clang__
|
||||
#define fallthrough do {} while (0) /* fall through */
|
||||
#else
|
||||
#define fallthrough __attribute__((__fallthrough__))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#undef HAVE_LINUX_TYPES_H
|
||||
@ -343,10 +349,6 @@ static inline uint64_t bswap_64(uint64_t val)
|
||||
snprintf(buf, len, #member)
|
||||
|
||||
/* these are defined in kernel */
|
||||
#ifndef PAGE_SIZE
|
||||
#define PAGE_SIZE 4096
|
||||
#endif
|
||||
#define PAGE_CACHE_SIZE 4096
|
||||
#define BITS_PER_BYTE 8
|
||||
#ifndef SECTOR_SHIFT
|
||||
#define SECTOR_SHIFT 9
|
||||
@ -478,7 +480,6 @@ struct f2fs_configuration {
|
||||
uint64_t wanted_total_sectors;
|
||||
uint64_t wanted_sector_size;
|
||||
uint64_t target_sectors;
|
||||
uint64_t max_size;
|
||||
uint32_t sectors_per_blk;
|
||||
uint32_t blks_per_seg;
|
||||
__u8 init_version[VERSION_LEN + 1];
|
||||
@ -507,6 +508,9 @@ struct f2fs_configuration {
|
||||
int force;
|
||||
int defset;
|
||||
int bug_on;
|
||||
int force_stop;
|
||||
int abnormal_stop;
|
||||
int fs_errors;
|
||||
int bug_nat_bits;
|
||||
bool quota_fixed;
|
||||
int alloc_failed;
|
||||
@ -756,6 +760,42 @@ struct f2fs_device {
|
||||
|
||||
static_assert(sizeof(struct f2fs_device) == 68, "");
|
||||
|
||||
/* reason of stop_checkpoint */
|
||||
enum stop_cp_reason {
|
||||
STOP_CP_REASON_SHUTDOWN,
|
||||
STOP_CP_REASON_FAULT_INJECT,
|
||||
STOP_CP_REASON_META_PAGE,
|
||||
STOP_CP_REASON_WRITE_FAIL,
|
||||
STOP_CP_REASON_CORRUPTED_SUMMARY,
|
||||
STOP_CP_REASON_UPDATE_INODE,
|
||||
STOP_CP_REASON_FLUSH_FAIL,
|
||||
STOP_CP_REASON_MAX,
|
||||
};
|
||||
|
||||
#define MAX_STOP_REASON 32
|
||||
|
||||
/* detail reason for EFSCORRUPTED */
|
||||
enum f2fs_error {
|
||||
ERROR_CORRUPTED_CLUSTER,
|
||||
ERROR_FAIL_DECOMPRESSION,
|
||||
ERROR_INVALID_BLKADDR,
|
||||
ERROR_CORRUPTED_DIRENT,
|
||||
ERROR_CORRUPTED_INODE,
|
||||
ERROR_INCONSISTENT_SUMMARY,
|
||||
ERROR_INCONSISTENT_FOOTER,
|
||||
ERROR_INCONSISTENT_SUM_TYPE,
|
||||
ERROR_CORRUPTED_JOURNAL,
|
||||
ERROR_INCONSISTENT_NODE_COUNT,
|
||||
ERROR_INCONSISTENT_BLOCK_COUNT,
|
||||
ERROR_INVALID_CURSEG,
|
||||
ERROR_INCONSISTENT_SIT,
|
||||
ERROR_CORRUPTED_VERITY_XATTR,
|
||||
ERROR_CORRUPTED_XATTR,
|
||||
ERROR_MAX,
|
||||
};
|
||||
|
||||
#define MAX_F2FS_ERRORS 16
|
||||
|
||||
struct f2fs_super_block {
|
||||
__le32 magic; /* Magic Number */
|
||||
__le16 major_ver; /* Major Version */
|
||||
@ -800,7 +840,9 @@ struct f2fs_super_block {
|
||||
__u8 hot_ext_count; /* # of hot file extension */
|
||||
__le16 s_encoding; /* Filename charset encoding */
|
||||
__le16 s_encoding_flags; /* Filename charset encoding flags */
|
||||
__u8 reserved[306]; /* valid reserved region */
|
||||
__u8 s_stop_reason[MAX_STOP_REASON]; /* stop checkpoint reason */
|
||||
__u8 s_errors[MAX_F2FS_ERRORS]; /* reason of image corrupts */
|
||||
__u8 reserved[258]; /* valid reserved region */
|
||||
__le32 crc; /* checksum of superblock */
|
||||
};
|
||||
|
||||
@ -943,9 +985,10 @@ static_assert(sizeof(struct f2fs_extent) == 12, "");
|
||||
DEFAULT_INLINE_XATTR_ADDRS - \
|
||||
F2FS_TOTAL_EXTRA_ATTR_SIZE - \
|
||||
DEF_INLINE_RESERVED_SIZE))
|
||||
#define INLINE_DATA_OFFSET (PAGE_CACHE_SIZE - sizeof(struct node_footer) \
|
||||
- sizeof(__le32)*(DEF_ADDRS_PER_INODE + 5 - \
|
||||
DEF_INLINE_RESERVED_SIZE))
|
||||
#define INLINE_DATA_OFFSET (F2FS_BLKSIZE - \
|
||||
sizeof(struct node_footer) - \
|
||||
sizeof(__le32) * (DEF_ADDRS_PER_INODE + \
|
||||
5 - DEF_INLINE_RESERVED_SIZE))
|
||||
|
||||
#define DEF_DIR_LEVEL 0
|
||||
|
||||
@ -1087,7 +1130,7 @@ static_assert(sizeof(struct f2fs_node) == 4096, "");
|
||||
/*
|
||||
* For NAT entries
|
||||
*/
|
||||
#define NAT_ENTRY_PER_BLOCK (PAGE_CACHE_SIZE / sizeof(struct f2fs_nat_entry))
|
||||
#define NAT_ENTRY_PER_BLOCK (F2FS_BLKSIZE / sizeof(struct f2fs_nat_entry))
|
||||
#define NAT_BLOCK_OFFSET(start_nid) (start_nid / NAT_ENTRY_PER_BLOCK)
|
||||
|
||||
#define DEFAULT_NAT_ENTRY_RATIO 20
|
||||
@ -1114,7 +1157,7 @@ static_assert(sizeof(struct f2fs_nat_block) == 4095, "");
|
||||
* Not allow to change this.
|
||||
*/
|
||||
#define SIT_VBLOCK_MAP_SIZE 64
|
||||
#define SIT_ENTRY_PER_BLOCK (PAGE_CACHE_SIZE / sizeof(struct f2fs_sit_entry))
|
||||
#define SIT_ENTRY_PER_BLOCK (F2FS_BLKSIZE / sizeof(struct f2fs_sit_entry))
|
||||
|
||||
/*
|
||||
* F2FS uses 4 bytes to represent block address. As a result, supported size of
|
||||
@ -1125,6 +1168,10 @@ static_assert(sizeof(struct f2fs_nat_block) == 4095, "");
|
||||
#define MAX_SIT_BITMAP_SIZE (SEG_ALIGN(SIZE_ALIGN(F2FS_MAX_SEGMENT, \
|
||||
SIT_ENTRY_PER_BLOCK)) * \
|
||||
c.blks_per_seg / 8)
|
||||
#define MAX_CP_PAYLOAD (SEG_ALIGN(SIZE_ALIGN(UINT32_MAX, NAT_ENTRY_PER_BLOCK)) * \
|
||||
DEFAULT_NAT_ENTRY_RATIO / 100 * \
|
||||
c.blks_per_seg / 8 + \
|
||||
MAX_SIT_BITMAP_SIZE - MAX_BITMAP_SIZE_IN_CKPT)
|
||||
|
||||
/*
|
||||
* Note that f2fs_sit_entry->vblocks has the following bit-field information.
|
||||
@ -1311,7 +1358,7 @@ typedef __le32 f2fs_hash_t;
|
||||
#define SIZE_OF_DIR_ENTRY 11 /* by byte */
|
||||
#define SIZE_OF_DENTRY_BITMAP ((NR_DENTRY_IN_BLOCK + BITS_PER_BYTE - 1) / \
|
||||
BITS_PER_BYTE)
|
||||
#define SIZE_OF_RESERVED (PAGE_SIZE - ((SIZE_OF_DIR_ENTRY + \
|
||||
#define SIZE_OF_RESERVED (F2FS_BLKSIZE - ((SIZE_OF_DIR_ENTRY + \
|
||||
F2FS_SLOT_LEN) * \
|
||||
NR_DENTRY_IN_BLOCK + SIZE_OF_DENTRY_BITMAP))
|
||||
#define MIN_INLINE_DENTRY_SIZE 40 /* just include '.' and '..' entries */
|
||||
@ -1335,7 +1382,7 @@ struct f2fs_dentry_block {
|
||||
__u8 filename[NR_DENTRY_IN_BLOCK][F2FS_SLOT_LEN];
|
||||
};
|
||||
|
||||
static_assert(sizeof(struct f2fs_dentry_block) == 4096, "");
|
||||
static_assert(sizeof(struct f2fs_dentry_block) == F2FS_BLKSIZE, "");
|
||||
|
||||
/* for inline stuff */
|
||||
#define DEF_INLINE_RESERVED_SIZE 1
|
||||
@ -1554,9 +1601,11 @@ blk_zone_cond_str(struct blk_zone *blkz)
|
||||
|
||||
#endif
|
||||
|
||||
struct blk_zone;
|
||||
|
||||
extern int f2fs_get_zoned_model(int);
|
||||
extern int f2fs_get_zone_blocks(int);
|
||||
extern int f2fs_report_zone(int, uint64_t, void *);
|
||||
extern int f2fs_report_zone(int, uint64_t, struct blk_zone *);
|
||||
typedef int (report_zones_cb_t)(int i, void *, void *);
|
||||
extern int f2fs_report_zones(int, report_zones_cb_t *, void *);
|
||||
extern int f2fs_check_zones(int);
|
||||
@ -1586,10 +1635,13 @@ static inline double get_best_overprovision(struct f2fs_super_block *sb)
|
||||
}
|
||||
|
||||
for (; candidate <= end; candidate += diff) {
|
||||
reserved = (2 * (100 / candidate + 1) + 6) *
|
||||
reserved = (100 / candidate + 1 + NR_CURSEG_TYPE) *
|
||||
round_up(usable_main_segs, get_sb(section_count));
|
||||
ovp = (usable_main_segs - reserved) * candidate / 100;
|
||||
space = usable_main_segs - reserved - ovp;
|
||||
if (ovp < 0)
|
||||
continue;
|
||||
space = usable_main_segs - max(reserved, ovp) -
|
||||
2 * get_sb(segs_per_sec);
|
||||
if (max_space < space) {
|
||||
max_space = space;
|
||||
max_ovp = candidate;
|
||||
|
@ -24,9 +24,6 @@
|
||||
#endif
|
||||
#include <time.h>
|
||||
#include <sys/stat.h>
|
||||
#ifdef HAVE_SYS_MOUNT_H
|
||||
#include <sys/mount.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_IOCTL_H
|
||||
#include <sys/ioctl.h>
|
||||
#endif
|
||||
@ -882,10 +879,12 @@ static int open_check_fs(char *path, int flag)
|
||||
return open(path, O_RDONLY | flag);
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
static int is_power_of_2(unsigned long n)
|
||||
{
|
||||
return (n != 0 && ((n & (n - 1)) == 0));
|
||||
}
|
||||
#endif
|
||||
|
||||
int get_device_info(int i)
|
||||
{
|
||||
@ -898,7 +897,7 @@ int get_device_info(int i)
|
||||
#ifdef HDIO_GETGIO
|
||||
struct hd_geometry geom;
|
||||
#endif
|
||||
#ifdef __linux__
|
||||
#if !defined(WITH_ANDROID) && defined(__linux__)
|
||||
sg_io_hdr_t io_hdr;
|
||||
unsigned char reply_buffer[96] = {0};
|
||||
unsigned char model_inq[6] = {MODELINQUIRY};
|
||||
@ -1000,7 +999,7 @@ int get_device_info(int i)
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
#if !defined(WITH_ANDROID) && defined(__linux__)
|
||||
/* Send INQUIRY command */
|
||||
memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
|
||||
io_hdr.interface_id = 'S';
|
||||
@ -1048,12 +1047,9 @@ int get_device_info(int i)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!is_power_of_2(dev->zone_size)) {
|
||||
MSG(0, "\tError: zoned: illegal zone size %lu (not a power of 2)\n",
|
||||
if (!is_power_of_2(dev->zone_size))
|
||||
MSG(0, "Info: zoned: zone size %" PRIu64 "u (not a power of 2)\n",
|
||||
dev->zone_size);
|
||||
free(stat_buf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check zone configuration: for the first disk of a
|
||||
@ -1067,10 +1063,10 @@ int get_device_info(int i)
|
||||
MSG(0, "Info: Host-%s zoned block device:\n",
|
||||
(dev->zoned_model == F2FS_ZONED_HA) ?
|
||||
"aware" : "managed");
|
||||
MSG(0, " %u zones, %lu zone size(bytes), %u randomly writeable zones\n",
|
||||
MSG(0, " %u zones, %" PRIu64 "u zone size(bytes), %u randomly writeable zones\n",
|
||||
dev->nr_zones, dev->zone_size,
|
||||
dev->nr_rnd_zones);
|
||||
MSG(0, " %lu blocks per zone\n",
|
||||
MSG(0, " %zu blocks per zone\n",
|
||||
dev->zone_blocks);
|
||||
}
|
||||
#endif
|
||||
|
@ -521,9 +521,6 @@ int dev_read(void *buf, __u64 offset, size_t len)
|
||||
int fd;
|
||||
int err;
|
||||
|
||||
if (c.max_size < (offset + len))
|
||||
c.max_size = offset + len;
|
||||
|
||||
if (c.sparse_mode)
|
||||
return sparse_read_blk(offset / F2FS_BLKSIZE,
|
||||
len / F2FS_BLKSIZE, buf);
|
||||
@ -563,9 +560,6 @@ int dev_write(void *buf, __u64 offset, size_t len)
|
||||
{
|
||||
int fd;
|
||||
|
||||
if (c.max_size < (offset + len))
|
||||
c.max_size = offset + len;
|
||||
|
||||
if (c.dry_run)
|
||||
return 0;
|
||||
|
||||
@ -604,9 +598,6 @@ int dev_fill(void *buf, __u64 offset, size_t len)
|
||||
{
|
||||
int fd;
|
||||
|
||||
if (c.max_size < (offset + len))
|
||||
c.max_size = offset + len;
|
||||
|
||||
if (c.sparse_mode)
|
||||
return sparse_write_zeroed_blk(offset / F2FS_BLKSIZE,
|
||||
len / F2FS_BLKSIZE);
|
||||
|
@ -29,8 +29,6 @@
|
||||
#endif
|
||||
#include <libgen.h>
|
||||
|
||||
#include <f2fs_fs.h>
|
||||
|
||||
#ifdef HAVE_LINUX_BLKZONED_H
|
||||
|
||||
int get_sysfs_path(struct device_info *dev, const char *attr,
|
||||
@ -204,20 +202,26 @@ int f2fs_get_zone_blocks(int i)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int f2fs_report_zone(int i, uint64_t sector, void *blkzone)
|
||||
int f2fs_report_zone(int i, uint64_t sector, struct blk_zone *blkzone)
|
||||
{
|
||||
struct blk_zone *blkz = (struct blk_zone *)blkzone;
|
||||
struct blk_zone_report *rep;
|
||||
struct one_zone_report {
|
||||
struct blk_zone_report rep;
|
||||
struct blk_zone zone;
|
||||
} *rep;
|
||||
int ret = -1;
|
||||
|
||||
rep = malloc(sizeof(struct blk_zone_report) + sizeof(struct blk_zone));
|
||||
static_assert(sizeof(*rep) == sizeof(rep->rep) + sizeof(rep->zone), "");
|
||||
|
||||
rep = calloc(1, sizeof(*rep));
|
||||
if (!rep) {
|
||||
ERR_MSG("No memory for report zones\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
rep->sector = sector;
|
||||
rep->nr_zones = 1;
|
||||
rep->rep = (struct blk_zone_report){
|
||||
.sector = sector,
|
||||
.nr_zones = 1,
|
||||
};
|
||||
ret = ioctl(c.devices[i].fd, BLKREPORTZONE, rep);
|
||||
if (ret != 0) {
|
||||
ret = -errno;
|
||||
@ -225,7 +229,7 @@ int f2fs_report_zone(int i, uint64_t sector, void *blkzone)
|
||||
goto out;
|
||||
}
|
||||
|
||||
*blkz = *(struct blk_zone *)(rep + 1);
|
||||
*blkzone = rep->zone;
|
||||
out:
|
||||
free(rep);
|
||||
return ret;
|
||||
@ -534,7 +538,8 @@ uint32_t f2fs_get_usable_segments(struct f2fs_super_block *sb)
|
||||
|
||||
#else
|
||||
|
||||
int f2fs_report_zone(int i, uint64_t UNUSED(sector), void *UNUSED(blkzone))
|
||||
int f2fs_report_zone(int i, uint64_t UNUSED(sector),
|
||||
struct blk_zone *UNUSED(blkzone))
|
||||
{
|
||||
ERR_MSG("%d: Unsupported zoned block device\n", i);
|
||||
return -1;
|
||||
|
@ -20,14 +20,13 @@
|
||||
* implementation.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "f2fs_fs.h"
|
||||
#include <f2fs_fs.h>
|
||||
|
||||
/* Encoding a unicode version number as a single unsigned int. */
|
||||
#define UNICODE_MAJ_SHIFT (16)
|
||||
|
@ -10,6 +10,10 @@ dump.f2fs \- retrieve directory and file entries from an F2FS-formated image
|
||||
.I inode number
|
||||
]
|
||||
[
|
||||
.B \-I
|
||||
.I inode number
|
||||
]
|
||||
[
|
||||
.B \-n
|
||||
.I NAT range
|
||||
]
|
||||
@ -52,6 +56,9 @@ is 0 on success and -1 on failure.
|
||||
.BI \-i " inode number"
|
||||
Specify an inode number to dump out.
|
||||
.TP
|
||||
.BI \-I " inode number"
|
||||
Specify an inode number and scan full disk to dump out, include history inode block
|
||||
.TP
|
||||
.BI \-n " NAT range"
|
||||
Specify a range presented by nids to dump NAT entries.
|
||||
.TP
|
||||
|
@ -76,6 +76,9 @@ buffered I/O
|
||||
.TP
|
||||
.B dio
|
||||
direct I/O
|
||||
.TP
|
||||
.B dsync
|
||||
direct I/O with O_DSYNC
|
||||
.RE
|
||||
.TP
|
||||
\fBread\fR \fI[chunk_size in 4kb] [offset in chunk_size] [count] [IO] [print_nbytes] [file_path]\fR
|
||||
@ -129,6 +132,9 @@ Release compressed blocks to get free space.
|
||||
.TP
|
||||
\fBreserve_cblocks\fR \fI[file]\fR
|
||||
Reserve free blocks to prepare decompressing blocks in the file.
|
||||
.TP
|
||||
\fBgc\fR \fI[sync_mode] [file]\fR
|
||||
Trigger filesystem GC
|
||||
.SH AUTHOR
|
||||
This version of
|
||||
.B f2fs_io
|
||||
|
@ -13,6 +13,19 @@ resize.f2fs \- resize filesystem size
|
||||
.B \-d
|
||||
.I debugging-level
|
||||
]
|
||||
[
|
||||
.B \-o
|
||||
.I overprovision-ratio-percentage
|
||||
]
|
||||
[
|
||||
.B \-i
|
||||
]
|
||||
[
|
||||
.B \-s
|
||||
]
|
||||
[
|
||||
.B \-V
|
||||
]
|
||||
.I device
|
||||
.SH DESCRIPTION
|
||||
.B resize.f2fs
|
||||
@ -35,6 +48,20 @@ Specify the size in sectors.
|
||||
Specify the level of debugging options.
|
||||
The default number is 0, which shows basic debugging messages.
|
||||
.TP
|
||||
.BI \-o " overprovision-ratio-percentage"
|
||||
Specify the percentage of the volume that will be used as overprovision area.
|
||||
This area is hidden to users, and utilized by F2FS cleaner. If not specified, the
|
||||
best number will be assigned automatically according to the partition size.
|
||||
.TP
|
||||
.BI \-i
|
||||
Enable extended node bitmap.
|
||||
.TP
|
||||
.BI \-s
|
||||
Enable safe resize.
|
||||
.TP
|
||||
.BI \-V
|
||||
Print the version number and exit.
|
||||
.TP
|
||||
.SH AUTHOR
|
||||
This version of
|
||||
.B resize.f2fs
|
||||
|
@ -15,6 +15,8 @@
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <f2fs_fs.h>
|
||||
|
||||
#ifdef HAVE_SYS_STAT_H
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
@ -23,7 +25,6 @@
|
||||
#endif
|
||||
#include <time.h>
|
||||
|
||||
#include "config.h"
|
||||
#ifdef HAVE_UUID_UUID_H
|
||||
#include <uuid/uuid.h>
|
||||
#endif
|
||||
@ -32,7 +33,6 @@
|
||||
#define uuid_generate(a)
|
||||
#endif
|
||||
|
||||
#include "f2fs_fs.h"
|
||||
#include "quota.h"
|
||||
#include "f2fs_format_utils.h"
|
||||
|
||||
@ -456,7 +456,7 @@ static int f2fs_prepare_super_block(void)
|
||||
* not overlap to metadata area.
|
||||
*/
|
||||
for (i = 1; i < c.ndevs; i++) {
|
||||
if (c.devices[i].zoned_model == F2FS_ZONED_HM &&
|
||||
if (c.devices[i].zoned_model != F2FS_ZONED_NONE &&
|
||||
c.devices[i].start_blkaddr < get_sb(main_blkaddr)) {
|
||||
MSG(0, "\tError: Conventional device %s is too small,"
|
||||
" (%"PRIu64" MiB needed).\n", c.devices[0].path,
|
||||
@ -469,7 +469,8 @@ static int f2fs_prepare_super_block(void)
|
||||
|
||||
total_zones = get_sb(segment_count) / (c.segs_per_zone) -
|
||||
total_meta_zones;
|
||||
|
||||
if (total_zones == 0)
|
||||
goto too_small;
|
||||
set_sb(section_count, total_zones * c.secs_per_zone);
|
||||
|
||||
set_sb(segment_count_main, get_sb(section_count) * c.segs_per_sec);
|
||||
@ -485,7 +486,7 @@ static int f2fs_prepare_super_block(void)
|
||||
c.overprovision = get_best_overprovision(sb);
|
||||
|
||||
c.reserved_segments =
|
||||
(2 * (100 / c.overprovision + 1) + NR_CURSEG_TYPE) *
|
||||
(100 / c.overprovision + 1 + NR_CURSEG_TYPE) *
|
||||
round_up(f2fs_get_usable_segments(sb), get_sb(section_count));
|
||||
|
||||
if (c.feature & cpu_to_le32(F2FS_FEATURE_RO)) {
|
||||
@ -499,8 +500,7 @@ static int f2fs_prepare_super_block(void)
|
||||
c.sector_size < zone_align_start_offset) ||
|
||||
(get_sb(segment_count_main) - NR_CURSEG_TYPE) <
|
||||
c.reserved_segments) {
|
||||
MSG(0, "\tError: Device size is not sufficient for F2FS volume\n");
|
||||
return -1;
|
||||
goto too_small;
|
||||
}
|
||||
|
||||
if (c.vol_uuid) {
|
||||
@ -546,10 +546,10 @@ static int f2fs_prepare_super_block(void)
|
||||
}
|
||||
|
||||
if (c.feature & cpu_to_le32(F2FS_FEATURE_RO)) {
|
||||
c.cur_seg[CURSEG_HOT_NODE] = 0;
|
||||
c.cur_seg[CURSEG_HOT_NODE] = last_section(last_zone(total_zones));
|
||||
c.cur_seg[CURSEG_WARM_NODE] = 0;
|
||||
c.cur_seg[CURSEG_COLD_NODE] = 0;
|
||||
c.cur_seg[CURSEG_HOT_DATA] = 1;
|
||||
c.cur_seg[CURSEG_HOT_DATA] = 0;
|
||||
c.cur_seg[CURSEG_COLD_DATA] = 0;
|
||||
c.cur_seg[CURSEG_WARM_DATA] = 0;
|
||||
} else if (c.heap) {
|
||||
@ -614,6 +614,10 @@ static int f2fs_prepare_super_block(void)
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
too_small:
|
||||
MSG(0, "\tError: Device size is not sufficient for F2FS volume\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int f2fs_init_sit_area(void)
|
||||
@ -762,8 +766,12 @@ static int f2fs_write_check_point_pack(void)
|
||||
set_cp(overprov_segment_count, (f2fs_get_usable_segments(sb) -
|
||||
get_cp(rsvd_segment_count)) *
|
||||
c.overprovision / 100);
|
||||
|
||||
if (get_cp(overprov_segment_count) < get_cp(rsvd_segment_count))
|
||||
set_cp(overprov_segment_count, get_cp(rsvd_segment_count));
|
||||
|
||||
set_cp(overprov_segment_count, get_cp(overprov_segment_count) +
|
||||
get_cp(rsvd_segment_count));
|
||||
2 * get_sb(segs_per_sec));
|
||||
|
||||
if (f2fs_get_usable_segments(sb) <= get_cp(overprov_segment_count)) {
|
||||
MSG(0, "\tError: Not enough segments to create F2FS Volume\n");
|
||||
|
@ -24,7 +24,8 @@
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
|
||||
#include "config.h"
|
||||
#include <f2fs_fs.h>
|
||||
|
||||
#ifdef HAVE_LIBBLKID
|
||||
#include <blkid/blkid.h>
|
||||
#endif
|
||||
@ -32,7 +33,6 @@
|
||||
#include <uuid/uuid.h>
|
||||
#endif
|
||||
|
||||
#include "f2fs_fs.h"
|
||||
#include "quota.h"
|
||||
#include "f2fs_format_utils.h"
|
||||
|
||||
@ -482,7 +482,7 @@ int main(int argc, char *argv[])
|
||||
* Some options are mandatory for host-managed
|
||||
* zoned block devices.
|
||||
*/
|
||||
if (c.zoned_model == F2FS_ZONED_HM && !c.zoned_mode) {
|
||||
if (c.zoned_model != F2FS_ZONED_NONE && !c.zoned_mode) {
|
||||
MSG(0, "\tError: zoned block device feature is required\n");
|
||||
goto err_format;
|
||||
}
|
||||
|
@ -48,10 +48,10 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
static int trim_device(int i)
|
||||
{
|
||||
#if defined(FALLOC_FL_PUNCH_HOLE) || defined(BLKDISCARD) || \
|
||||
defined(BLKSECDISCARD)
|
||||
static int trim_device(int i)
|
||||
{
|
||||
unsigned long long range[2];
|
||||
struct stat *stat_buf;
|
||||
struct device_info *dev = c.devices + i;
|
||||
@ -110,13 +110,18 @@ static int trim_device(int i)
|
||||
}
|
||||
#endif
|
||||
free(stat_buf);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static int trim_device(int UNUSED(i))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef WITH_ANDROID
|
||||
static bool is_wiped_device(int i)
|
||||
{
|
||||
#ifdef WITH_ANDROID
|
||||
struct device_info *dev = c.devices + i;
|
||||
int fd = dev->fd;
|
||||
char *buf, *zero_buf;
|
||||
@ -124,6 +129,10 @@ static bool is_wiped_device(int i)
|
||||
int nblocks = 4096; /* 16MB size */
|
||||
int j;
|
||||
|
||||
/* let's trim the other devices except the first device */
|
||||
if (i > 0)
|
||||
return false;
|
||||
|
||||
buf = malloc(F2FS_BLKSIZE);
|
||||
if (buf == NULL) {
|
||||
MSG(1, "\tError: Malloc Failed for buf!!!\n");
|
||||
@ -156,10 +165,13 @@ static bool is_wiped_device(int i)
|
||||
if (wiped)
|
||||
MSG(0, "Info: Found all zeros in first %d blocks\n", nblocks);
|
||||
return wiped;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
static bool is_wiped_device(int UNUSED(i))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
int f2fs_trim_devices(void)
|
||||
{
|
||||
|
@ -2,11 +2,10 @@
|
||||
|
||||
AM_CPPFLAGS = ${libuuid_CFLAGS} -I$(top_srcdir)/include
|
||||
AM_CFLAGS = -Wall
|
||||
sbin_PROGRAMS = f2fstat
|
||||
sbin_PROGRAMS =
|
||||
if !WINDOWS
|
||||
sbin_PROGRAMS += fibmap.f2fs parse.f2fs
|
||||
endif
|
||||
f2fstat_SOURCES = f2fstat.c
|
||||
fibmap_f2fs_SOURCES = fibmap.c
|
||||
parse_f2fs_SOURCES = f2fs_io_parse.c
|
||||
|
||||
@ -17,4 +16,4 @@ f2fscrypt_LDFLAGS = ${libuuid_LIBS}
|
||||
dist_man_MANS = f2fscrypt.8
|
||||
endif
|
||||
|
||||
SUBDIRS = sg_write_buffer f2fs_io
|
||||
SUBDIRS = f2fs_io
|
||||
|
@ -1,7 +1,7 @@
|
||||
## Makefile.am
|
||||
|
||||
if LINUX
|
||||
AM_CPPFLAGS = -I../../include
|
||||
AM_CPPFLAGS = -I$(top_srcdir)/include
|
||||
AM_CFLAGS = -Wall
|
||||
sbin_PROGRAMS = f2fs_io
|
||||
f2fs_io_SOURCES = f2fs_io.c
|
||||
|
@ -43,7 +43,7 @@
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#include <config.h>
|
||||
#endif
|
||||
#include <android_config.h>
|
||||
|
||||
@ -508,11 +508,14 @@ static void do_erase(int argc, char **argv, const struct cmd_desc *cmd)
|
||||
" rand : random numbers\n" \
|
||||
"IO can be\n" \
|
||||
" buffered : buffered IO\n" \
|
||||
" dio : direct IO\n" \
|
||||
" dio : O_DIRECT\n" \
|
||||
" dsync : O_DIRECT | O_DSYNC\n" \
|
||||
" osync : O_SYNC\n" \
|
||||
" atomic_commit : atomic write & commit\n" \
|
||||
" atomic_abort : atomic write & abort\n" \
|
||||
"{delay} is in ms unit and optional only for atomic_commit and atomic_abort\n"
|
||||
" atomic_rcommit: atomic replace & commit\n" \
|
||||
" atomic_rabort : atomic replace & abort\n" \
|
||||
"{delay} is in ms unit and optional only for atomic operations\n"
|
||||
|
||||
static void do_write(int argc, char **argv, const struct cmd_desc *cmd)
|
||||
{
|
||||
@ -523,7 +526,7 @@ static void do_write(int argc, char **argv, const struct cmd_desc *cmd)
|
||||
int flags = 0;
|
||||
int fd;
|
||||
u64 total_time = 0, max_time = 0, max_time_t = 0;
|
||||
bool atomic_commit = false, atomic_abort = false;
|
||||
bool atomic_commit = false, atomic_abort = false, replace = false;
|
||||
int useconds = 0;
|
||||
|
||||
srand(time(0));
|
||||
@ -550,16 +553,25 @@ static void do_write(int argc, char **argv, const struct cmd_desc *cmd)
|
||||
else if (strcmp(argv[4], "inc_num") && strcmp(argv[4], "rand"))
|
||||
die("Wrong pattern type");
|
||||
|
||||
if (!strcmp(argv[5], "dio"))
|
||||
if (!strcmp(argv[5], "dio")) {
|
||||
flags |= O_DIRECT;
|
||||
else if (!strcmp(argv[5], "osync"))
|
||||
} else if (!strcmp(argv[5], "dsync")) {
|
||||
flags |= O_DIRECT | O_DSYNC;
|
||||
} else if (!strcmp(argv[5], "osync")) {
|
||||
flags |= O_SYNC;
|
||||
else if (!strcmp(argv[5], "atomic_commit"))
|
||||
} else if (!strcmp(argv[5], "atomic_commit")) {
|
||||
atomic_commit = true;
|
||||
else if (!strcmp(argv[5], "atomic_abort"))
|
||||
} else if (!strcmp(argv[5], "atomic_abort")) {
|
||||
atomic_abort = true;
|
||||
else if (strcmp(argv[5], "buffered"))
|
||||
} else if (!strcmp(argv[5], "atomic_rcommit")) {
|
||||
atomic_commit = true;
|
||||
replace = true;
|
||||
} else if (!strcmp(argv[5], "atomic_rabort")) {
|
||||
atomic_abort = true;
|
||||
replace = true;
|
||||
} else if (strcmp(argv[5], "buffered")) {
|
||||
die("Wrong IO type");
|
||||
}
|
||||
|
||||
fd = xopen(argv[6], O_CREAT | O_WRONLY | flags, 0755);
|
||||
|
||||
@ -569,7 +581,11 @@ static void do_write(int argc, char **argv, const struct cmd_desc *cmd)
|
||||
if (argc == 8)
|
||||
useconds = atoi(argv[7]) * 1000;
|
||||
|
||||
ret = ioctl(fd, F2FS_IOC_START_ATOMIC_WRITE);
|
||||
if (replace)
|
||||
ret = ioctl(fd, F2FS_IOC_START_ATOMIC_REPLACE);
|
||||
else
|
||||
ret = ioctl(fd, F2FS_IOC_START_ATOMIC_WRITE);
|
||||
|
||||
if (ret < 0) {
|
||||
fputs("setting atomic file mode failed\n", stderr);
|
||||
exit(1);
|
||||
@ -1179,16 +1195,17 @@ static void do_get_filename_encrypt_mode (int argc, char **argv,
|
||||
const struct cmd_desc *cmd)
|
||||
{
|
||||
static const char *enc_name[] = {
|
||||
"invalid", /* FS_ENCRYPTION_MODE_INVALID (0) */
|
||||
"aes-256-xts", /* FS_ENCRYPTION_MODE_AES_256_XTS (1) */
|
||||
"aes-256-gcm", /* FS_ENCRYPTION_MODE_AES_256_GCM (2) */
|
||||
"aes-256-cbc", /* FS_ENCRYPTION_MODE_AES_256_CBC (3) */
|
||||
"aes-256-cts", /* FS_ENCRYPTION_MODE_AES_256_CTS (4) */
|
||||
"aes-128-cbc", /* FS_ENCRYPTION_MODE_AES_128_CBC (5) */
|
||||
"aes-128-cts", /* FS_ENCRYPTION_MODE_AES_128_CTS (6) */
|
||||
"speck128-256-xts", /* FS_ENCRYPTION_MODE_SPECK128_256_XTS (7) */
|
||||
"speck128-256-cts", /* FS_ENCRYPTION_MODE_SPECK128_256_CTS (8) */
|
||||
"adiantum", /* FS_ENCRYPTION_MODE_ADIANTUM (9) */
|
||||
"invalid", /* FSCRYPT_MODE_INVALID (0) */
|
||||
"aes-256-xts", /* FSCRYPT_MODE_AES_256_XTS (1) */
|
||||
"aes-256-gcm", /* FSCRYPT_MODE_AES_256_GCM (2) */
|
||||
"aes-256-cbc", /* FSCRYPT_MODE_AES_256_CBC (3) */
|
||||
"aes-256-cts", /* FSCRYPT_MODE_AES_256_CTS (4) */
|
||||
"aes-128-cbc", /* FSCRYPT_MODE_AES_128_CBC (5) */
|
||||
"aes-128-cts", /* FSCRYPT_MODE_AES_128_CTS (6) */
|
||||
"speck128-256-xts", /* FSCRYPT_MODE_SPECK128_256_XTS (7) */
|
||||
"speck128-256-cts", /* FSCRYPT_MODE_SPECK128_256_CTS (8) */
|
||||
"adiantum", /* FSCRYPT_MODE_ADIANTUM (9) */
|
||||
"aes-256-hctr2", /* FSCRYPT_MODE_AES_256_HCTR2 (10) */
|
||||
};
|
||||
int fd, mode, ret;
|
||||
struct fscrypt_get_policy_ex_arg arg;
|
||||
@ -1267,6 +1284,33 @@ static void do_rename(int argc, char **argv, const struct cmd_desc *cmd)
|
||||
exit(0);
|
||||
}
|
||||
|
||||
#define gc_desc "trigger filesystem GC"
|
||||
#define gc_help "f2fs_io gc sync_mode [file_path]\n\n"
|
||||
|
||||
static void do_gc(int argc, char **argv, const struct cmd_desc *cmd)
|
||||
{
|
||||
u32 sync;
|
||||
int ret, fd;
|
||||
|
||||
if (argc != 3) {
|
||||
fputs("Excess arguments\n\n", stderr);
|
||||
fputs(cmd->cmd_help, stderr);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
sync = atoi(argv[1]);
|
||||
|
||||
fd = xopen(argv[2], O_RDONLY, 0);
|
||||
|
||||
ret = ioctl(fd, F2FS_IOC_GARBAGE_COLLECT, &sync);
|
||||
if (ret < 0)
|
||||
die_errno("F2FS_IOC_GARBAGE_COLLECT failed");
|
||||
|
||||
printf("trigger %s gc ret=%d\n",
|
||||
sync ? "synchronous" : "asynchronous", ret);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
#define CMD_HIDDEN 0x0001
|
||||
#define CMD(name) { #name, do_##name, name##_desc, name##_help, 0 }
|
||||
#define _CMD(name) { #name, do_##name, NULL, NULL, CMD_HIDDEN }
|
||||
@ -1298,6 +1342,7 @@ const struct cmd_desc cmd_list[] = {
|
||||
CMD(compress),
|
||||
CMD(get_filename_encrypt_mode),
|
||||
CMD(rename),
|
||||
CMD(gc),
|
||||
{ NULL, NULL, NULL, NULL, 0 }
|
||||
};
|
||||
|
||||
|
@ -90,6 +90,7 @@ typedef u32 __be32;
|
||||
struct f2fs_comp_option)
|
||||
#define F2FS_IOC_DECOMPRESS_FILE _IO(F2FS_IOCTL_MAGIC, 23)
|
||||
#define F2FS_IOC_COMPRESS_FILE _IO(F2FS_IOCTL_MAGIC, 24)
|
||||
#define F2FS_IOC_START_ATOMIC_REPLACE _IO(F2FS_IOCTL_MAGIC, 25)
|
||||
|
||||
#ifndef FSCRYPT_POLICY_V1
|
||||
#define FSCRYPT_POLICY_V1 0
|
||||
|
@ -20,7 +20,8 @@
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
#include "config.h"
|
||||
#include <f2fs_fs.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
@ -43,6 +44,7 @@
|
||||
#ifdef __KERNEL__
|
||||
#include <linux/fs.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_UUID_UUID_H
|
||||
#include <uuid/uuid.h>
|
||||
#else
|
||||
@ -55,7 +57,6 @@ typedef unsigned char uuid_t[16];
|
||||
#ifdef HAVE_SYS_KEY_H
|
||||
#include <sys/key.h>
|
||||
#endif
|
||||
#include <f2fs_fs.h>
|
||||
|
||||
#define F2FS_MAX_KEY_SIZE 64
|
||||
#define F2FS_MAX_PASSPHRASE_SIZE 1024
|
||||
|
311
tools/f2fstat.c
311
tools/f2fstat.c
@ -1,311 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <libgen.h>
|
||||
|
||||
#ifdef DEBUG
|
||||
#define dbg(fmt, args...) printf(fmt, __VA_ARGS__);
|
||||
#else
|
||||
#define dbg(fmt, args...)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* f2fs status
|
||||
*/
|
||||
#define F2FS_STATUS "/sys/kernel/debug/f2fs/status"
|
||||
|
||||
#define KEY_NODE 0x00000001
|
||||
#define KEY_META 0x00000010
|
||||
|
||||
unsigned long util;
|
||||
unsigned long used_node_blks;
|
||||
unsigned long used_data_blks;
|
||||
//unsigned long inline_inode;
|
||||
|
||||
unsigned long free_segs;
|
||||
unsigned long valid_segs;
|
||||
unsigned long dirty_segs;
|
||||
unsigned long prefree_segs;
|
||||
|
||||
unsigned long gc, bg_gc;
|
||||
unsigned long cp;
|
||||
unsigned long gc_data_blks;
|
||||
unsigned long gc_node_blks;
|
||||
|
||||
//unsigned long extent_hit_ratio;
|
||||
|
||||
unsigned long dirty_node, node_kb;
|
||||
unsigned long dirty_dents;
|
||||
unsigned long dirty_meta, meta_kb;
|
||||
unsigned long nat_caches;
|
||||
unsigned long dirty_sit;
|
||||
|
||||
unsigned long free_nids;
|
||||
|
||||
unsigned long ssr_blks;
|
||||
unsigned long lfs_blks;
|
||||
unsigned long memory_kb;
|
||||
|
||||
struct options {
|
||||
int delay;
|
||||
int interval;
|
||||
char partname[32];
|
||||
};
|
||||
|
||||
struct mm_table {
|
||||
const char *name;
|
||||
unsigned long *val;
|
||||
int flag;
|
||||
};
|
||||
|
||||
static int compare_mm_table(const void *a, const void *b)
|
||||
{
|
||||
dbg("[COMPARE] %s, %s\n", ((struct mm_table *)a)->name, ((struct mm_table *)b)->name);
|
||||
return strcmp(((struct mm_table *)a)->name, ((struct mm_table *)b)->name);
|
||||
}
|
||||
|
||||
static inline void remove_newline(char **head)
|
||||
{
|
||||
again:
|
||||
if (**head == '\n') {
|
||||
*head = *head + 1;
|
||||
goto again;
|
||||
}
|
||||
}
|
||||
|
||||
void f2fstat(struct options *opt)
|
||||
{
|
||||
int fd;
|
||||
int ret;
|
||||
char keyname[32];
|
||||
char buf[4096];
|
||||
struct mm_table key = { keyname, NULL, 0 };
|
||||
struct mm_table *found;
|
||||
int f2fstat_table_cnt;
|
||||
char *head, *tail;
|
||||
int found_cnt = 0;
|
||||
|
||||
static struct mm_table f2fstat_table[] = {
|
||||
{ " - Data", &used_data_blks, 0 },
|
||||
{ " - Dirty", &dirty_segs, 0 },
|
||||
{ " - Free", &free_segs, 0 },
|
||||
{ " - NATs", &nat_caches, 0 },
|
||||
{ " - Node", &used_node_blks, 0 },
|
||||
{ " - Prefree", &prefree_segs, 0 },
|
||||
{ " - SITs", &dirty_sit, 0 },
|
||||
{ " - Valid", &valid_segs, 0 },
|
||||
{ " - dents", &dirty_dents, 0 },
|
||||
{ " - free_nids", &free_nids, 0 },
|
||||
{ " - meta", &dirty_meta, KEY_META },
|
||||
{ " - nodes", &dirty_node, KEY_NODE },
|
||||
{ "CP calls", &cp, 0 },
|
||||
{ "GC calls", &gc, 0 },
|
||||
{ "LFS", &lfs_blks, 0 },
|
||||
{ "Memory", &memory_kb, 0 },
|
||||
{ "SSR", &ssr_blks, 0 },
|
||||
{ "Utilization", &util, 0 },
|
||||
};
|
||||
|
||||
f2fstat_table_cnt = sizeof(f2fstat_table)/sizeof(struct mm_table);
|
||||
|
||||
fd = open(F2FS_STATUS, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
perror("open " F2FS_STATUS);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
ret = read(fd, buf, 4096);
|
||||
if (ret < 0) {
|
||||
perror("read " F2FS_STATUS);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
buf[ret] = '\0';
|
||||
|
||||
head = buf;
|
||||
|
||||
if (opt->partname[0] != '\0') {
|
||||
head = strstr(buf, opt->partname);
|
||||
if (head == NULL)
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
remove_newline(&head);
|
||||
tail = strchr(head, ':');
|
||||
if (!tail)
|
||||
break;
|
||||
*tail = '\0';
|
||||
if (strlen(head) >= sizeof(keyname)) {
|
||||
dbg("[OVER] %s\n", head);
|
||||
*tail = ':';
|
||||
tail = strchr(head, '\n');
|
||||
head = tail + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
strcpy(keyname, head);
|
||||
|
||||
found = bsearch(&key, f2fstat_table, f2fstat_table_cnt, sizeof(struct mm_table), compare_mm_table);
|
||||
dbg("[RESULT] %s (%s)\n", head, (found) ? "O" : "X");
|
||||
head = tail + 1;
|
||||
if (!found)
|
||||
goto nextline;
|
||||
|
||||
*(found->val) = strtoul(head, &tail, 10);
|
||||
if (found->flag) {
|
||||
int npages;
|
||||
tail = strstr(head, "in");
|
||||
head = tail + 2;
|
||||
npages = strtoul(head, &tail, 10);
|
||||
switch (found->flag & (KEY_NODE | KEY_META)) {
|
||||
case KEY_NODE:
|
||||
node_kb = npages * 4;
|
||||
break;
|
||||
case KEY_META:
|
||||
meta_kb = npages * 4;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (++found_cnt == f2fstat_table_cnt)
|
||||
break;
|
||||
nextline:
|
||||
tail = strchr(head, '\n');
|
||||
if (!tail)
|
||||
break;
|
||||
head = tail + 1;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
}
|
||||
|
||||
void usage(void)
|
||||
{
|
||||
printf("Usage: f2fstat [option]\n"
|
||||
" -d delay (secs)\n"
|
||||
" -i interval of head info\n"
|
||||
" -p partition name (e.g. /dev/sda3)\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
void parse_option(int argc, char *argv[], struct options *opt)
|
||||
{
|
||||
int option;
|
||||
const char *option_string = "d:i:p:h";
|
||||
|
||||
while ((option = getopt(argc, argv, option_string)) != EOF) {
|
||||
switch (option) {
|
||||
case 'd':
|
||||
opt->delay = atoi(optarg);
|
||||
break;
|
||||
case 'i':
|
||||
opt->interval = atoi(optarg);
|
||||
break;
|
||||
case 'p':
|
||||
strcpy(opt->partname, basename(optarg));
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void __make_head(char *head, int index, int i, int len)
|
||||
{
|
||||
char name_h[5][20] = {"main segments", "page/slab caches", "cp/gc", "blks", "memory"};
|
||||
int half = (len - strlen(name_h[i])) / 2;
|
||||
|
||||
*(head + index) = '|';
|
||||
index++;
|
||||
memset(head + index, '-', half);
|
||||
index += half;
|
||||
strcpy(head + index, name_h[i]);
|
||||
index += strlen(name_h[i]);
|
||||
memset(head + index, '-', half);
|
||||
}
|
||||
|
||||
void print_head(char *res)
|
||||
{
|
||||
char *ptr, *ptr_buf;
|
||||
char buf[1024], head[1024];
|
||||
char name[20][10] = {"util", "node", "data", "free", "valid", "dirty", "prefree", "node", "dent", "meta",
|
||||
"sit", "nat", "fnid", "cp", "gc", "ssr", "lfs", "total", "node", "meta"};
|
||||
int i, len, prev_index = 0;
|
||||
|
||||
ptr_buf = buf;
|
||||
memset(buf, ' ', 1024);
|
||||
memset(head, ' ', 1024);
|
||||
|
||||
for (i = 0; i < 20; i++) {
|
||||
ptr = (i == 0) ? strtok(res, " ") : strtok(NULL, " ");
|
||||
strcpy(ptr_buf, name[i]);
|
||||
if (i == 1) {
|
||||
prev_index = ptr_buf - buf - 1;
|
||||
} else if (i == 7) {
|
||||
len = (ptr_buf - buf) - 1 - prev_index;
|
||||
__make_head(head, prev_index, 0, len);
|
||||
prev_index = ptr_buf - buf - 1;
|
||||
} else if (i == 13) {
|
||||
len = (ptr_buf - buf) - 1 - prev_index;
|
||||
__make_head(head, prev_index, 1, len);
|
||||
prev_index = ptr_buf - buf - 1;
|
||||
} else if (i == 15) {
|
||||
len = (ptr_buf - buf) - 1 - prev_index;
|
||||
__make_head(head, prev_index, 2, len);
|
||||
prev_index = ptr_buf - buf - 1;
|
||||
} else if (i == 17) {
|
||||
len = (ptr_buf - buf) - 1 - prev_index;
|
||||
__make_head(head, prev_index, 3, len);
|
||||
prev_index = ptr_buf - buf - 1;
|
||||
}
|
||||
|
||||
len = strlen(ptr);
|
||||
ptr_buf += (len > strlen(name[i]) ? len : strlen(name[i])) + 1;
|
||||
}
|
||||
|
||||
len = (ptr_buf - buf) - 1 - prev_index;
|
||||
__make_head(head, prev_index, 4, len);
|
||||
|
||||
*ptr_buf = 0;
|
||||
*(head + (ptr_buf - buf - 1)) = '|';
|
||||
*(head + (ptr_buf - buf)) = 0;
|
||||
fprintf(stderr, "%s\n%s\n", head, buf);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
char format[] = "%4ld %4ld %4ld %4ld %5ld %5ld %7ld %4ld %4ld %4ld %3ld %3ld %4ld %2ld %2ld %3ld %3ld %5ld %4ld %4ld";
|
||||
char buf[1024], tmp[1024];
|
||||
int head_interval;
|
||||
struct options opt = {
|
||||
.delay = 1,
|
||||
.interval = 20,
|
||||
.partname = { 0, },
|
||||
};
|
||||
|
||||
parse_option(argc, argv, &opt);
|
||||
head_interval = opt.interval;
|
||||
|
||||
while (1) {
|
||||
memset(buf, 0, 1024);
|
||||
f2fstat(&opt);
|
||||
sprintf(buf, format, util, used_node_blks, used_data_blks,
|
||||
free_segs, valid_segs, dirty_segs, prefree_segs,
|
||||
dirty_node, dirty_dents, dirty_meta, dirty_sit, nat_caches, free_nids,
|
||||
cp, gc, ssr_blks, lfs_blks, memory_kb, node_kb, meta_kb);
|
||||
|
||||
strcpy(tmp, buf);
|
||||
if (head_interval == opt.interval)
|
||||
print_head(tmp);
|
||||
if (head_interval-- == 0)
|
||||
head_interval = opt.interval;
|
||||
|
||||
fprintf(stderr, "%s\n", buf);
|
||||
|
||||
sleep(opt.delay);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -47,7 +47,7 @@
|
||||
#endif
|
||||
|
||||
struct file_ext {
|
||||
__u32 f_pos;
|
||||
__u64 f_pos;
|
||||
__u32 start_blk;
|
||||
__u32 end_blk;
|
||||
__u32 blk_count;
|
||||
@ -56,9 +56,9 @@ struct file_ext {
|
||||
void print_ext(struct file_ext *ext)
|
||||
{
|
||||
if (ext->end_blk == 0)
|
||||
printf("%8d %8d %8d %8d\n", ext->f_pos, 0, 0, ext->blk_count);
|
||||
printf("%8llu %8d %8d %8d\n", ext->f_pos, 0, 0, ext->blk_count);
|
||||
else
|
||||
printf("%8d %8d %8d %8d\n", ext->f_pos, ext->start_blk,
|
||||
printf("%8llu %8d %8d %8d\n", ext->f_pos, ext->start_blk,
|
||||
ext->end_blk, ext->blk_count);
|
||||
}
|
||||
|
||||
@ -102,7 +102,7 @@ static void stat_bdev(struct stat *st, unsigned int *start_lba)
|
||||
char linkname[32] = { 0, };
|
||||
int fd;
|
||||
|
||||
sprintf(devname, "/dev/block/%d:%d", major(st->st_dev), minor(st->st_dev));
|
||||
sprintf(devname, "/sys/dev/block/%d:%d", major(st->st_dev), minor(st->st_dev));
|
||||
|
||||
fd = open(devname, O_RDONLY);
|
||||
if (fd < 0)
|
||||
@ -209,7 +209,7 @@ int main(int argc, char *argv[])
|
||||
ext.blk_count++;
|
||||
} else {
|
||||
print_ext(&ext);
|
||||
ext.f_pos = i * st.st_blksize;
|
||||
ext.f_pos = (__u64)i * st.st_blksize;
|
||||
ext.start_blk = blknum;
|
||||
ext.end_blk = blknum;
|
||||
ext.blk_count = 1;
|
||||
|
@ -1,18 +0,0 @@
|
||||
## Makefile.am
|
||||
|
||||
if LINUX
|
||||
AM_CPPFLAGS = -I$(srcdir)/include
|
||||
AM_CFLAGS = -Wall
|
||||
sbin_PROGRAMS = sg_write_buffer
|
||||
sg_write_buffer_SOURCES = sg_write_buffer.c \
|
||||
sg_cmds_basic.c \
|
||||
sg_cmds_basic2.c \
|
||||
sg_cmds_extra.c \
|
||||
sg_cmds_mmc.c \
|
||||
sg_io_linux.c \
|
||||
sg_lib.c \
|
||||
sg_lib_data.c \
|
||||
sg_pt_common.c \
|
||||
sg_pt_linux.c \
|
||||
sg_pt_linux_nvme.c
|
||||
endif
|
@ -1,156 +0,0 @@
|
||||
PROPS-END
|
||||
/*-
|
||||
* Copyright (C) 2012-2013 Intel Corporation
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
|
||||
#include <sys/param.h>
|
||||
|
||||
#define NVME_PASSTHROUGH_CMD _IOWR('n', 0, struct nvme_pt_command)
|
||||
|
||||
#if __FreeBSD_version < 1100110
|
||||
struct nvme_command
|
||||
{
|
||||
/* dword 0 */
|
||||
uint16_t opc : 8; /* opcode */
|
||||
uint16_t fuse : 2; /* fused operation */
|
||||
uint16_t rsvd1 : 6;
|
||||
uint16_t cid; /* command identifier */
|
||||
|
||||
/* dword 1 */
|
||||
uint32_t nsid; /* namespace identifier */
|
||||
|
||||
/* dword 2-3 */
|
||||
uint32_t rsvd2;
|
||||
uint32_t rsvd3;
|
||||
|
||||
/* dword 4-5 */
|
||||
uint64_t mptr; /* metadata pointer */
|
||||
|
||||
/* dword 6-7 */
|
||||
uint64_t prp1; /* prp entry 1 */
|
||||
|
||||
/* dword 8-9 */
|
||||
uint64_t prp2; /* prp entry 2 */
|
||||
|
||||
/* dword 10-15 */
|
||||
uint32_t cdw10; /* command-specific */
|
||||
uint32_t cdw11; /* command-specific */
|
||||
uint32_t cdw12; /* command-specific */
|
||||
uint32_t cdw13; /* command-specific */
|
||||
uint32_t cdw14; /* command-specific */
|
||||
uint32_t cdw15; /* command-specific */
|
||||
} __packed;
|
||||
|
||||
struct nvme_status {
|
||||
|
||||
uint16_t p : 1; /* phase tag */
|
||||
uint16_t sc : 8; /* status code */
|
||||
uint16_t sct : 3; /* status code type */
|
||||
uint16_t rsvd2 : 2;
|
||||
uint16_t m : 1; /* more */
|
||||
uint16_t dnr : 1; /* do not retry */
|
||||
} __packed;
|
||||
|
||||
struct nvme_completion {
|
||||
|
||||
/* dword 0 */
|
||||
uint32_t cdw0; /* command-specific */
|
||||
|
||||
/* dword 1 */
|
||||
uint32_t rsvd1;
|
||||
|
||||
/* dword 2 */
|
||||
uint16_t sqhd; /* submission queue head pointer */
|
||||
uint16_t sqid; /* submission queue identifier */
|
||||
|
||||
/* dword 3 */
|
||||
uint16_t cid; /* command identifier */
|
||||
struct nvme_status status;
|
||||
} __packed;
|
||||
|
||||
struct nvme_pt_command {
|
||||
|
||||
/*
|
||||
* cmd is used to specify a passthrough command to a controller or
|
||||
* namespace.
|
||||
*
|
||||
* The following fields from cmd may be specified by the caller:
|
||||
* * opc (opcode)
|
||||
* * nsid (namespace id) - for admin commands only
|
||||
* * cdw10-cdw15
|
||||
*
|
||||
* Remaining fields must be set to 0 by the caller.
|
||||
*/
|
||||
struct nvme_command cmd;
|
||||
|
||||
/*
|
||||
* cpl returns completion status for the passthrough command
|
||||
* specified by cmd.
|
||||
*
|
||||
* The following fields will be filled out by the driver, for
|
||||
* consumption by the caller:
|
||||
* * cdw0
|
||||
* * status (except for phase)
|
||||
*
|
||||
* Remaining fields will be set to 0 by the driver.
|
||||
*/
|
||||
struct nvme_completion cpl;
|
||||
|
||||
/* buf is the data buffer associated with this passthrough command. */
|
||||
void * buf;
|
||||
|
||||
/*
|
||||
* len is the length of the data buffer associated with this
|
||||
* passthrough command.
|
||||
*/
|
||||
uint32_t len;
|
||||
|
||||
/*
|
||||
* is_read = 1 if the passthrough command will read data into the
|
||||
* supplied buffer from the controller.
|
||||
*
|
||||
* is_read = 0 if the passthrough command will write data from the
|
||||
* supplied buffer to the controller.
|
||||
*/
|
||||
uint32_t is_read;
|
||||
|
||||
/*
|
||||
* driver_lock is used by the driver only. It must be set to 0
|
||||
* by the caller.
|
||||
*/
|
||||
struct mtx * driver_lock;
|
||||
};
|
||||
#else
|
||||
#include <dev/nvme/nvme.h>
|
||||
#endif
|
||||
|
||||
#define nvme_completion_is_error(cpl) \
|
||||
((cpl)->status.sc != 0 || (cpl)->status.sct != 0)
|
||||
|
||||
#define NVME_CTRLR_PREFIX "/dev/nvme"
|
||||
#define NVME_NS_PREFIX "ns"
|
@ -1,21 +0,0 @@
|
||||
#ifndef SG_CMDS_H
|
||||
#define SG_CMDS_H
|
||||
|
||||
/********************************************************************
|
||||
* This header did contain wrapper declarations for many SCSI commands
|
||||
* up until sg3_utils version 1.22 . In that version, the command
|
||||
* wrappers were broken into two groups, the 'basic' ones found in the
|
||||
* "sg_cmds_basic.h" header and the 'extra' ones found in the
|
||||
* "sg_cmds_extra.h" header. This header now simply includes those two
|
||||
* headers.
|
||||
* In sg3_utils version 1.26 the sg_cmds_mmc.h header was added and
|
||||
* contains some MMC specific commands.
|
||||
* The corresponding function definitions are found in the sg_cmds_basic.c,
|
||||
* sg_cmds_extra.c and sg_cmds_mmc.c files.
|
||||
********************************************************************/
|
||||
|
||||
#include "sg_cmds_basic.h"
|
||||
#include "sg_cmds_extra.h"
|
||||
#include "sg_cmds_mmc.h"
|
||||
|
||||
#endif
|
@ -1,310 +0,0 @@
|
||||
#ifndef SG_CMDS_BASIC_H
|
||||
#define SG_CMDS_BASIC_H
|
||||
|
||||
/*
|
||||
* Copyright (c) 2004-2017 Douglas Gilbert.
|
||||
* All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style
|
||||
* license that can be found in the BSD_LICENSE file.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Error, warning and verbose output is sent to the file pointed to by
|
||||
* sg_warnings_strm which is declared in sg_lib.h and can be set with
|
||||
* the sg_set_warnings_strm() function. If not given sg_warnings_strm
|
||||
* defaults to stderr.
|
||||
* If 'noisy' is false and 'verbose' is zero then following functions should
|
||||
* not output anything to sg_warnings_strm. If 'noisy' is true and
|
||||
* 'verbose' is zero then Unit Attention, Recovered, Medium and Hardware
|
||||
* errors (sense keys) send output to sg_warnings_strm. Increasing values
|
||||
* of 'verbose' send increasing amounts of (debug) output to
|
||||
* sg_warnings_strm.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/* Invokes a SCSI INQUIRY command and yields the response
|
||||
* Returns 0 when successful, SG_LIB_CAT_INVALID_OP -> not supported,
|
||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb,
|
||||
* SG_LIB_CAT_ABORTED_COMMAND, -1 -> other errors */
|
||||
int sg_ll_inquiry(int sg_fd, bool cmddt, bool evpd, int pg_op, void * resp,
|
||||
int mx_resp_len, bool noisy, int verbose);
|
||||
|
||||
/* Invokes a SCSI INQUIRY command and yields the response. Returns 0 when
|
||||
* successful, various SG_LIB_CAT_* positive values or -1 -> other errors.
|
||||
* The CMDDT field is obsolete in the INQUIRY cdb (since spc3r16 in 2003) so
|
||||
* an argument to set it has been removed (use the REPORT SUPPORTED OPERATION
|
||||
* CODES command instead). Adds the ability to set the command abort timeout
|
||||
* and the ability to report the residual count. If timeout_secs is zero
|
||||
* or less the default command abort timeout (60 seconds) is used.
|
||||
* If residp is non-NULL then the residual value is written where residp
|
||||
* points. A residual value of 0 implies mx_resp_len bytes have be written
|
||||
* where resp points. If the residual value equals mx_resp_len then no
|
||||
* bytes have been written. */
|
||||
int
|
||||
sg_ll_inquiry_v2(int sg_fd, bool evpd, int pg_op, void * resp,
|
||||
int mx_resp_len, int timeout_secs, int * residp,
|
||||
bool noisy, int verbose);
|
||||
|
||||
/* Invokes a SCSI LOG SELECT command. Return of 0 -> success,
|
||||
* SG_LIB_CAT_INVALID_OP -> Log Select not supported,
|
||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
|
||||
* SG_LIB_CAT_ABORTED_COMMAND, * SG_LIB_CAT_NOT_READY -> device not ready,
|
||||
* -1 -> other failure */
|
||||
int sg_ll_log_select(int sg_fd, bool pcr, bool sp, int pc, int pg_code,
|
||||
int subpg_code, unsigned char * paramp, int param_len,
|
||||
bool noisy, int verbose);
|
||||
|
||||
/* Invokes a SCSI LOG SENSE command. Return of 0 -> success,
|
||||
* SG_LIB_CAT_INVALID_OP -> Log Sense not supported,
|
||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
|
||||
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
|
||||
* -1 -> other failure */
|
||||
int sg_ll_log_sense(int sg_fd, bool ppc, bool sp, int pc, int pg_code,
|
||||
int subpg_code, int paramp, unsigned char * resp,
|
||||
int mx_resp_len, bool noisy, int verbose);
|
||||
|
||||
/* Same as sg_ll_log_sense() apart from timeout_secs and residp. See
|
||||
* sg_ll_inquiry_v2() for their description */
|
||||
int sg_ll_log_sense_v2(int sg_fd, bool ppc, bool sp, int pc, int pg_code,
|
||||
int subpg_code, int paramp, unsigned char * resp,
|
||||
int mx_resp_len, int timeout_secs, int * residp,
|
||||
bool noisy, int verbose);
|
||||
|
||||
/* Invokes a SCSI MODE SELECT (6) command. Return of 0 -> success,
|
||||
* SG_LIB_CAT_INVALID_OP -> invalid opcode, SG_LIB_CAT_ILLEGAL_REQ ->
|
||||
* bad field in cdb, * SG_LIB_CAT_NOT_READY -> device not ready,
|
||||
* SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND,
|
||||
* -1 -> other failure */
|
||||
int sg_ll_mode_select6(int sg_fd, bool pf, bool sp, void * paramp,
|
||||
int param_len, bool noisy, int verbose);
|
||||
|
||||
/* Invokes a SCSI MODE SELECT (10) command. Return of 0 -> success,
|
||||
* SG_LIB_CAT_INVALID_OP -> invalid opcode, SG_LIB_CAT_ILLEGAL_REQ ->
|
||||
* bad field in cdb, * SG_LIB_CAT_NOT_READY -> device not ready,
|
||||
* SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND,
|
||||
* -1 -> other failure */
|
||||
int sg_ll_mode_select10(int sg_fd, bool pf, bool sp, void * paramp,
|
||||
int param_len, bool noisy, int verbose);
|
||||
|
||||
/* Invokes a SCSI MODE SENSE (6) command. Return of 0 -> success,
|
||||
* SG_LIB_CAT_INVALID_OP -> invalid opcode, SG_LIB_CAT_ILLEGAL_REQ ->
|
||||
* bad field in cdb, * SG_LIB_CAT_NOT_READY -> device not ready,
|
||||
* SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND,
|
||||
* -1 -> other failure */
|
||||
int sg_ll_mode_sense6(int sg_fd, bool dbd, int pc, int pg_code,
|
||||
int sub_pg_code, void * resp, int mx_resp_len,
|
||||
bool noisy, int verbose);
|
||||
|
||||
/* Invokes a SCSI MODE SENSE (10) command. Return of 0 -> success,
|
||||
* SG_LIB_CAT_INVALID_OP -> invalid opcode, SG_LIB_CAT_ILLEGAL_REQ ->
|
||||
* bad field in cdb, * SG_LIB_CAT_NOT_READY -> device not ready,
|
||||
* SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND,
|
||||
* -1 -> other failure */
|
||||
int sg_ll_mode_sense10(int sg_fd, bool llbaa, bool dbd, int pc, int pg_code,
|
||||
int sub_pg_code, void * resp, int mx_resp_len,
|
||||
bool noisy, int verbose);
|
||||
|
||||
/* Same as sg_ll_mode_sense10() apart from timeout_secs and residp. See
|
||||
* sg_ll_inquiry_v2() for their description */
|
||||
int sg_ll_mode_sense10_v2(int sg_fd, bool llbaa, bool dbd, int pc,
|
||||
int pg_code, int sub_pg_code, void * resp,
|
||||
int mx_resp_len, int timeout_secs, int * residp,
|
||||
bool noisy, int verbose);
|
||||
|
||||
/* Invokes a SCSI PREVENT ALLOW MEDIUM REMOVAL command (SPC-3)
|
||||
* prevent==0 allows removal, prevent==1 prevents removal ...
|
||||
* Return of 0 -> success,
|
||||
* SG_LIB_CAT_INVALID_OP -> command not supported
|
||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
|
||||
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
|
||||
* -1 -> other failure */
|
||||
int sg_ll_prevent_allow(int sg_fd, int prevent, bool noisy, int verbose);
|
||||
|
||||
/* Invokes a SCSI READ CAPACITY (10) command. Return of 0 -> success,
|
||||
* SG_LIB_CAT_INVALID_OP -> invalid opcode, SG_LIB_CAT_UNIT_ATTENTION
|
||||
* -> perhaps media changed, SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb,
|
||||
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
|
||||
* -1 -> other failure */
|
||||
int sg_ll_readcap_10(int sg_fd, bool pmi, unsigned int lba, void * resp,
|
||||
int mx_resp_len, bool noisy, int verbose);
|
||||
|
||||
/* Invokes a SCSI READ CAPACITY (16) command. Returns 0 -> success,
|
||||
* SG_LIB_CAT_UNIT_ATTENTION -> media changed??, SG_LIB_CAT_INVALID_OP
|
||||
* -> cdb not supported, SG_LIB_CAT_IlLEGAL_REQ -> bad field in cdb
|
||||
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
|
||||
* -1 -> other failure */
|
||||
int sg_ll_readcap_16(int sg_fd, bool pmi, uint64_t llba, void * resp,
|
||||
int mx_resp_len, bool noisy, int verbose);
|
||||
|
||||
/* Invokes a SCSI REPORT LUNS command. Return of 0 -> success,
|
||||
* SG_LIB_CAT_INVALID_OP -> Report Luns not supported,
|
||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND,
|
||||
* SG_LIB_NOT_READY (shouldn't happen), -1 -> other failure */
|
||||
int sg_ll_report_luns(int sg_fd, int select_report, void * resp,
|
||||
int mx_resp_len, bool noisy, int verbose);
|
||||
|
||||
/* Invokes a SCSI REQUEST SENSE command. Return of 0 -> success,
|
||||
* SG_LIB_CAT_INVALID_OP -> Request Sense not supported??,
|
||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND,
|
||||
* -1 -> other failure */
|
||||
int sg_ll_request_sense(int sg_fd, bool desc, void * resp, int mx_resp_len,
|
||||
bool noisy, int verbose);
|
||||
|
||||
/* Invokes a SCSI START STOP UNIT command (SBC + MMC).
|
||||
* Return of 0 -> success,
|
||||
* SG_LIB_CAT_INVALID_OP -> Start stop unit not supported,
|
||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
|
||||
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
|
||||
* -1 -> other failure
|
||||
* SBC-3 and MMC partially overlap on the power_condition_modifier(sbc) and
|
||||
* format_layer_number(mmc) fields. They also overlap on the noflush(sbc)
|
||||
* and fl(mmc) one bit field. This is the cause of the awkardly named
|
||||
* pc_mod__fl_num and noflush__fl arguments to this function. */
|
||||
int sg_ll_start_stop_unit(int sg_fd, bool immed, int pc_mod__fl_num,
|
||||
int power_cond, bool noflush__fl, bool loej,
|
||||
bool start, bool noisy, int verbose);
|
||||
|
||||
/* Invokes a SCSI SYNCHRONIZE CACHE (10) command. Return of 0 -> success,
|
||||
* SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND,
|
||||
* SG_LIB_CAT_INVALID_OP -> cdb not supported,
|
||||
* SG_LIB_CAT_IlLEGAL_REQ -> bad field in cdb
|
||||
* SG_LIB_CAT_NOT_READY -> device not ready, -1 -> other failure */
|
||||
int sg_ll_sync_cache_10(int sg_fd, bool sync_nv, bool immed, int group,
|
||||
unsigned int lba, unsigned int count, bool noisy,
|
||||
int verbose);
|
||||
|
||||
/* Invokes a SCSI TEST UNIT READY command.
|
||||
* 'pack_id' is just for diagnostics, safe to set to 0.
|
||||
* Return of 0 -> success, SG_LIB_CAT_UNIT_ATTENTION,
|
||||
* SG_LIB_CAT_NOT_READY -> device not ready,
|
||||
* SG_LIB_CAT_ABORTED_COMMAND, -1 -> other failure */
|
||||
int sg_ll_test_unit_ready(int sg_fd, int pack_id, bool noisy, int verbose);
|
||||
|
||||
/* Invokes a SCSI TEST UNIT READY command.
|
||||
* 'pack_id' is just for diagnostics, safe to set to 0.
|
||||
* Looks for progress indicator if 'progress' non-NULL;
|
||||
* if found writes value [0..65535] else write -1.
|
||||
* Return of 0 -> success, SG_LIB_CAT_UNIT_ATTENTION,
|
||||
* SG_LIB_CAT_ABORTED_COMMAND, SG_LIB_CAT_NOT_READY ->
|
||||
* device not ready, -1 -> other failure */
|
||||
int sg_ll_test_unit_ready_progress(int sg_fd, int pack_id, int * progress,
|
||||
bool noisy, int verbose);
|
||||
|
||||
|
||||
struct sg_simple_inquiry_resp {
|
||||
unsigned char peripheral_qualifier;
|
||||
unsigned char peripheral_type;
|
||||
unsigned char byte_1; /* was 'rmb' prior to version 1.39 */
|
||||
/* now rmb == !!(0x80 & byte_1) */
|
||||
unsigned char version; /* as per recent drafts: whole of byte 2 */
|
||||
unsigned char byte_3;
|
||||
unsigned char byte_5;
|
||||
unsigned char byte_6;
|
||||
unsigned char byte_7;
|
||||
char vendor[9]; /* T10 field is 8 bytes, NUL char appended */
|
||||
char product[17];
|
||||
char revision[5];
|
||||
};
|
||||
|
||||
/* Yields most of first 36 bytes of a standard INQUIRY (evpd==0) response.
|
||||
* Returns 0 when successful, SG_LIB_CAT_INVALID_OP -> not supported,
|
||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND,
|
||||
* -1 -> other errors */
|
||||
int sg_simple_inquiry(int sg_fd, struct sg_simple_inquiry_resp * inq_data,
|
||||
bool noisy, int verbose);
|
||||
|
||||
/* MODE SENSE commands yield a response that has header then zero or more
|
||||
* block descriptors followed by mode pages. In most cases users are
|
||||
* interested in the first mode page. This function returns the (byte)
|
||||
* offset of the start of the first mode page. Set mode_sense_6 to true for
|
||||
* MODE SENSE (6) and false for MODE SENSE (10). Returns >= 0 is successful
|
||||
* or -1 if failure. If there is a failure a message is written to err_buff
|
||||
* if it is non-NULL and err_buff_len > 0. */
|
||||
int sg_mode_page_offset(const unsigned char * resp, int resp_len,
|
||||
bool mode_sense_6, char * err_buff, int err_buff_len);
|
||||
|
||||
/* MODE SENSE commands yield a response that has header then zero or more
|
||||
* block descriptors followed by mode pages. This functions returns the
|
||||
* length (in bytes) of those three components. Note that the return value
|
||||
* can exceed resp_len in which case the MODE SENSE command should be
|
||||
* re-issued with a larger response buffer. If bd_lenp is non-NULL and if
|
||||
* successful the block descriptor length (in bytes) is written to *bd_lenp.
|
||||
* Set mode_sense_6 to true for MODE SENSE (6) and false for MODE SENSE (10)
|
||||
* responses. Returns -1 if there is an error (e.g. response too short). */
|
||||
int sg_msense_calc_length(const unsigned char * resp, int resp_len,
|
||||
bool mode_sense_6, int * bd_lenp);
|
||||
|
||||
/* Fetches current, changeable, default and/or saveable modes pages as
|
||||
* indicated by pcontrol_arr for given pg_code and sub_pg_code. If
|
||||
* mode6==0 then use MODE SENSE (10) else use MODE SENSE (6). If
|
||||
* flexible set and mode data length seems wrong then try and
|
||||
* fix (compensating hack for bad device or driver). pcontrol_arr
|
||||
* should have 4 elements for output of current, changeable, default
|
||||
* and saved values respectively. Each element should be NULL or
|
||||
* at least mx_mpage_len bytes long.
|
||||
* Return of 0 -> overall success, SG_LIB_CAT_INVALID_OP -> invalid opcode,
|
||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
|
||||
* SG_LIB_CAT_NOT_READY -> device not ready,
|
||||
* SG_LIB_CAT_MALFORMED -> bad response, -1 -> other failure.
|
||||
* If success_mask pointer is not NULL then first zeros it. Then set bits
|
||||
* 0, 1, 2 and/or 3 if the current, changeable, default and saved values
|
||||
* respectively have been fetched. If error on current page
|
||||
* then stops and returns that error; otherwise continues if an error is
|
||||
* detected but returns the first error encountered. */
|
||||
int sg_get_mode_page_controls(int sg_fd, bool mode6, int pg_code,
|
||||
int sub_pg_code, bool dbd, bool flexible,
|
||||
int mx_mpage_len, int * success_mask,
|
||||
void * pcontrol_arr[], int * reported_lenp,
|
||||
int verbose);
|
||||
|
||||
/* Returns file descriptor >= 0 if successful. If error in Unix returns
|
||||
negated errno. Implementation calls scsi_pt_open_device(). */
|
||||
int sg_cmds_open_device(const char * device_name, bool read_only, int verbose);
|
||||
|
||||
/* Returns file descriptor >= 0 if successful. If error in Unix returns
|
||||
negated errno. Implementation calls scsi_pt_open_flags(). */
|
||||
int sg_cmds_open_flags(const char * device_name, int flags, int verbose);
|
||||
|
||||
/* Returns 0 if successful. If error in Unix returns negated errno.
|
||||
Implementation calls scsi_pt_close_device(). */
|
||||
int sg_cmds_close_device(int device_fd);
|
||||
|
||||
const char * sg_cmds_version();
|
||||
|
||||
#define SG_NO_DATA_IN 0
|
||||
|
||||
struct sg_pt_base;
|
||||
|
||||
/* This is a helper function used by sg_cmds_* implementations after the
|
||||
* call to the pass-through. pt_res is returned from do_scsi_pt(). If valid
|
||||
* sense data is found it is decoded and output to sg_warnings_strm (def:
|
||||
* stderr); depending on the 'noisy' and 'verbose' settings. Returns -2 for
|
||||
* sense data (may not be fatal), -1 for failed, 0, or a positive number. If
|
||||
* 'mx_di_len > 0' then asks pass-through for resid and returns
|
||||
* (mx_di_len - resid); otherwise returns 0. So for data-in it should return
|
||||
* the actual number of bytes received. For data-out (to device) or no data
|
||||
* call with 'mx_di_len' set to 0 or less. If -2 returned then sense category
|
||||
* output via 'o_sense_cat' pointer (if not NULL). Note that several sense
|
||||
* categories also have data in bytes received; -2 is still returned. */
|
||||
int sg_cmds_process_resp(struct sg_pt_base * ptvp, const char * leadin,
|
||||
int pt_res, int mx_di_len,
|
||||
const unsigned char * sense_b, bool noisy,
|
||||
int verbose, int * o_sense_cat);
|
||||
|
||||
/* NVMe devices use a different command set. This function will return true
|
||||
* if the device associated with 'pvtp' is a NVME device, else it will
|
||||
* return false (e.g. for SCSI devices). */
|
||||
bool sg_cmds_is_nvme(const struct sg_pt_base * ptvp);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
@ -1,369 +0,0 @@
|
||||
#ifndef SG_CMDS_EXTRA_H
|
||||
#define SG_CMDS_EXTRA_H
|
||||
|
||||
/*
|
||||
* Copyright (c) 2004-2018 Douglas Gilbert.
|
||||
* All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style
|
||||
* license that can be found in the BSD_LICENSE file.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Note: all functions that have an 'int timeout_secs' argument will use
|
||||
* that value if it is > 0. Otherwise they will set an internal default
|
||||
* which is currently 60 seconds. This timeout is typically applied in the
|
||||
* SCSI stack above the initiator. If it goes off then the SCSI command is
|
||||
* aborted and there can be other unwelcome side effects. Note that some
|
||||
* commands (e.g. FORMAT UNIT and the Third Party copy commands) can take
|
||||
* a lot longer than the default timeout. */
|
||||
|
||||
|
||||
/* Invokes a ATA PASS-THROUGH (12, 16 or 32) SCSI command (SAT). This is
|
||||
* selected by the cdb_len argument that can take values of 12, 16 or 32
|
||||
* only (else -1 is returned). The byte at offset 0 (and bytes 0 to 9
|
||||
* inclusive for ATA PT(32)) pointed to be cdbp are ignored and apart from
|
||||
* the control byte, the rest is copied into an internal cdb which is then
|
||||
* sent to the device. The control byte is byte 11 for ATA PT(12), byte 15
|
||||
* for ATA PT(16) and byte 1 for ATA PT(32). If timeout_secs <= 0 then the
|
||||
* timeout is set to 60 seconds. For data in or out transfers set dinp or
|
||||
* doutp, and dlen to the number of bytes to transfer. If dlen is zero then
|
||||
* no data transfer is assumed. If sense buffer obtained then it is written
|
||||
* to sensep, else sensep[0] is set to 0x0. If ATA return descriptor is
|
||||
* obtained then written to ata_return_dp, else ata_return_dp[0] is set to
|
||||
* 0x0. Either sensep or ata_return_dp (or both) may be NULL pointers.
|
||||
* Returns SCSI status value (>= 0) or -1 if other error. Users are
|
||||
* expected to check the sense buffer themselves. If available the data in
|
||||
* resid is written to residp. Note in SAT-2 and later, fixed format sense
|
||||
* data may be placed in *sensep in which case sensep[0]==0x70, prior to
|
||||
* SAT-2 descriptor sense format was required (i.e. sensep[0]==0x72).
|
||||
*/
|
||||
int sg_ll_ata_pt(int sg_fd, const unsigned char * cdbp, int cdb_len,
|
||||
int timeout_secs, void * dinp, void * doutp, int dlen,
|
||||
unsigned char * sensep, int max_sense_len,
|
||||
unsigned char * ata_return_dp, int max_ata_return_len,
|
||||
int * residp, int verbose);
|
||||
|
||||
/* Invokes a FORMAT UNIT (SBC-3) command. Return of 0 -> success,
|
||||
* SG_LIB_CAT_INVALID_OP -> Format unit not supported,
|
||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
|
||||
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
|
||||
* -1 -> other failure. Note that sg_ll_format_unit2() and
|
||||
* sg_ll_format_unit_v2() are the same, both add the ffmt argument. */
|
||||
int sg_ll_format_unit(int sg_fd, int fmtpinfo, bool longlist, bool fmtdata,
|
||||
bool cmplist, int dlist_format, int timeout_secs,
|
||||
void * paramp, int param_len, bool noisy, int verbose);
|
||||
int sg_ll_format_unit2(int sg_fd, int fmtpinfo, bool longlist, bool fmtdata,
|
||||
bool cmplist, int dlist_format, int ffmt,
|
||||
int timeout_secs, void * paramp, int param_len,
|
||||
bool noisy, int verbose);
|
||||
int sg_ll_format_unit_v2(int sg_fd, int fmtpinfo, bool longlist, bool fmtdata,
|
||||
bool cmplist, int dlist_format, int ffmt,
|
||||
int timeout_secs, void * paramp, int param_len,
|
||||
bool noisy, int verbose);
|
||||
|
||||
/* Invokes a SCSI GET LBA STATUS(16) or GET LBA STATUS(32) command (SBC).
|
||||
* Returns 0 -> success,
|
||||
* SG_LIB_CAT_INVALID_OP -> GET LBA STATUS(16 or 32) not supported,
|
||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND,
|
||||
* SG_LIB_CAT_NOT_READY -> device not ready, -1 -> other failure.
|
||||
* sg_ll_get_lba_status() calls the 16 byte variant with rt=0 . */
|
||||
int sg_ll_get_lba_status(int sg_fd, uint64_t start_llba, void * resp,
|
||||
int alloc_len, bool noisy, int verbose);
|
||||
int sg_ll_get_lba_status16(int sg_fd, uint64_t start_llba, uint8_t rt,
|
||||
void * resp, int alloc_len, bool noisy,
|
||||
int verbose);
|
||||
int sg_ll_get_lba_status32(int sg_fd, uint64_t start_llba, uint32_t scan_len,
|
||||
uint32_t element_id, uint8_t rt,
|
||||
void * resp, int alloc_len, bool noisy,
|
||||
int verbose);
|
||||
|
||||
/* Invokes a SCSI PERSISTENT RESERVE IN command (SPC). Returns 0
|
||||
* when successful, SG_LIB_CAT_INVALID_OP if command not supported,
|
||||
* SG_LIB_CAT_ILLEGAL_REQ if field in cdb not supported,
|
||||
* SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND, else -1 */
|
||||
int sg_ll_persistent_reserve_in(int sg_fd, int rq_servact, void * resp,
|
||||
int mx_resp_len, bool noisy, int verbose);
|
||||
|
||||
/* Invokes a SCSI PERSISTENT RESERVE OUT command (SPC). Returns 0
|
||||
* when successful, SG_LIB_CAT_INVALID_OP if command not supported,
|
||||
* SG_LIB_CAT_ILLEGAL_REQ if field in cdb not supported,
|
||||
* SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND, else -1 */
|
||||
int sg_ll_persistent_reserve_out(int sg_fd, int rq_servact, int rq_scope,
|
||||
unsigned int rq_type, void * paramp,
|
||||
int param_len, bool noisy, int verbose);
|
||||
|
||||
/* Invokes a SCSI READ BLOCK LIMITS command. Return of 0 -> success,
|
||||
* SG_LIB_CAT_INVALID_OP -> READ BLOCK LIMITS not supported,
|
||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND,
|
||||
* SG_LIB_NOT_READY (shouldn't happen), -1 -> other failure */
|
||||
int sg_ll_read_block_limits(int sg_fd, void * resp, int mx_resp_len,
|
||||
bool noisy, int verbose);
|
||||
|
||||
/* Invokes a SCSI READ BUFFER command (SPC). Return of 0 ->
|
||||
* success, SG_LIB_CAT_INVALID_OP -> invalid opcode,
|
||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
|
||||
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
|
||||
* -1 -> other failure */
|
||||
int sg_ll_read_buffer(int sg_fd, int mode, int buffer_id, int buffer_offset,
|
||||
void * resp, int mx_resp_len, bool noisy, int verbose);
|
||||
|
||||
/* Invokes a SCSI READ DEFECT DATA (10) command (SBC). Return of 0 ->
|
||||
* success, SG_LIB_CAT_INVALID_OP -> invalid opcode,
|
||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
|
||||
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
|
||||
* -1 -> other failure */
|
||||
int sg_ll_read_defect10(int sg_fd, bool req_plist, bool req_glist,
|
||||
int dl_format, void * resp, int mx_resp_len,
|
||||
bool noisy, int verbose);
|
||||
|
||||
/* Invokes a SCSI READ LONG (10) command (SBC). Note that 'xfer_len'
|
||||
* is in bytes. Returns 0 -> success,
|
||||
* SG_LIB_CAT_INVALID_OP -> READ LONG(10) not supported,
|
||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb,
|
||||
* SG_LIB_CAT_ILLEGAL_REQ_WITH_INFO -> bad field in cdb, with info
|
||||
* field written to 'offsetp', SG_LIB_CAT_UNIT_ATTENTION,
|
||||
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
|
||||
* -1 -> other failure */
|
||||
int sg_ll_read_long10(int sg_fd, bool pblock, bool correct, unsigned int lba,
|
||||
void * resp, int xfer_len, int * offsetp, bool noisy,
|
||||
int verbose);
|
||||
|
||||
/* Invokes a SCSI READ LONG (16) command (SBC). Note that 'xfer_len'
|
||||
* is in bytes. Returns 0 -> success,
|
||||
* SG_LIB_CAT_INVALID_OP -> READ LONG(16) not supported,
|
||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb,
|
||||
* SG_LIB_CAT_ILLEGAL_REQ_WITH_INFO -> bad field in cdb, with info
|
||||
* field written to 'offsetp', SG_LIB_CAT_UNIT_ATTENTION,
|
||||
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
|
||||
* -1 -> other failure */
|
||||
int sg_ll_read_long16(int sg_fd, bool pblock, bool correct, uint64_t llba,
|
||||
void * resp, int xfer_len, int * offsetp, bool noisy,
|
||||
int verbose);
|
||||
|
||||
/* Invokes a SCSI READ MEDIA SERIAL NUMBER command. Return of 0 -> success,
|
||||
* SG_LIB_CAT_INVALID_OP -> Read media serial number not supported,
|
||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
|
||||
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
|
||||
* -1 -> other failure */
|
||||
int sg_ll_read_media_serial_num(int sg_fd, void * resp, int mx_resp_len,
|
||||
bool noisy, int verbose);
|
||||
|
||||
/* Invokes a SCSI REASSIGN BLOCKS command. Return of 0 -> success,
|
||||
* SG_LIB_CAT_INVALID_OP -> invalid opcode, SG_LIB_CAT_UNIT_ATTENTION,
|
||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND,
|
||||
* SG_LIB_CAT_NOT_READY -> device not ready, -1 -> other failure */
|
||||
int sg_ll_reassign_blocks(int sg_fd, bool longlba, bool longlist,
|
||||
void * paramp, int param_len, bool noisy,
|
||||
int verbose);
|
||||
|
||||
/* Invokes a SCSI RECEIVE DIAGNOSTIC RESULTS command. Return of 0 -> success,
|
||||
* SG_LIB_CAT_INVALID_OP -> Receive diagnostic results not supported,
|
||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
|
||||
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
|
||||
* -1 -> other failure */
|
||||
int sg_ll_receive_diag(int sg_fd, bool pcv, int pg_code, void * resp,
|
||||
int mx_resp_len, bool noisy, int verbose);
|
||||
|
||||
/* Same as sg_ll_receive_diag() but with added timeout_secs and residp
|
||||
* arguments. Adds the ability to set the command abort timeout
|
||||
* and the ability to report the residual count. If timeout_secs is zero
|
||||
* or less the default command abort timeout (60 seconds) is used.
|
||||
* If residp is non-NULL then the residual value is written where residp
|
||||
* points. A residual value of 0 implies mx_resp_len bytes have be written
|
||||
* where resp points. If the residual value equals mx_resp_len then no
|
||||
* bytes have been written. */
|
||||
int sg_ll_receive_diag_v2(int sg_fd, bool pcv, int pg_code, void * resp,
|
||||
int mx_resp_len, int timeout_secs, int * residp,
|
||||
bool noisy, int verbose);
|
||||
|
||||
/* Invokes a SCSI REPORT IDENTIFYING INFORMATION command. This command was
|
||||
* called REPORT DEVICE IDENTIFIER prior to spc4r07. Return of 0 -> success,
|
||||
* SG_LIB_CAT_INVALID_OP -> Report identifying information not supported,
|
||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
|
||||
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
|
||||
* -1 -> other failure */
|
||||
int sg_ll_report_id_info(int sg_fd, int itype, void * resp, int max_resp_len,
|
||||
bool noisy, int verbose);
|
||||
|
||||
/* Invokes a SCSI REPORT TARGET PORT GROUPS command. Return of 0 -> success,
|
||||
* SG_LIB_CAT_INVALID_OP -> Report Target Port Groups not supported,
|
||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND,
|
||||
* SG_LIB_CAT_UNIT_ATTENTION, -1 -> other failure */
|
||||
int sg_ll_report_tgt_prt_grp(int sg_fd, void * resp, int mx_resp_len,
|
||||
bool noisy, int verbose);
|
||||
int sg_ll_report_tgt_prt_grp2(int sg_fd, void * resp, int mx_resp_len,
|
||||
bool extended, bool noisy, int verbose);
|
||||
|
||||
/* Invokes a SCSI SET TARGET PORT GROUPS command. Return of 0 -> success,
|
||||
* SG_LIB_CAT_INVALID_OP -> Report Target Port Groups not supported,
|
||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND,
|
||||
* SG_LIB_CAT_UNIT_ATTENTION, -1 -> other failure */
|
||||
int sg_ll_set_tgt_prt_grp(int sg_fd, void * paramp, int param_len, bool noisy,
|
||||
int verbose);
|
||||
|
||||
/* Invokes a SCSI REPORT REFERRALS command. Return of 0 -> success,
|
||||
* SG_LIB_CAT_INVALID_OP -> Report Referrals not supported,
|
||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND,
|
||||
* SG_LIB_CAT_UNIT_ATTENTION, -1 -> other failure */
|
||||
int sg_ll_report_referrals(int sg_fd, uint64_t start_llba, bool one_seg,
|
||||
void * resp, int mx_resp_len, bool noisy,
|
||||
int verbose);
|
||||
|
||||
/* Invokes a SCSI SEND DIAGNOSTIC command. Foreground, extended self tests can
|
||||
* take a long time, if so set long_duration flag in which case the timeout
|
||||
* is set to 7200 seconds; if the value of long_duration is > 7200 then that
|
||||
* value is taken as the timeout value in seconds. Return of 0 -> success,
|
||||
* SG_LIB_CAT_INVALID_OP -> Send diagnostic not supported,
|
||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
|
||||
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
|
||||
* -1 -> other failure */
|
||||
int sg_ll_send_diag(int sg_fd, int st_code, bool pf_bit, bool st_bit,
|
||||
bool devofl_bit, bool unitofl_bit, int long_duration,
|
||||
void * paramp, int param_len, bool noisy, int verbose);
|
||||
|
||||
/* Invokes a SCSI SET IDENTIFYING INFORMATION command. This command was
|
||||
* called SET DEVICE IDENTIFIER prior to spc4r07. Return of 0 -> success,
|
||||
* SG_LIB_CAT_INVALID_OP -> Set identifying information not supported,
|
||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
|
||||
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
|
||||
* -1 -> other failure */
|
||||
int sg_ll_set_id_info(int sg_fd, int itype, void * paramp, int param_len,
|
||||
bool noisy, int verbose);
|
||||
|
||||
/* Invokes a SCSI UNMAP (SBC-3) command. Return of 0 -> success,
|
||||
* SG_LIB_CAT_INVALID_OP -> command not supported,
|
||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND,
|
||||
* SG_LIB_CAT_UNIT_ATTENTION, -1 -> other failure */
|
||||
int sg_ll_unmap(int sg_fd, int group_num, int timeout_secs, void * paramp,
|
||||
int param_len, bool noisy, int verbose);
|
||||
/* Invokes a SCSI UNMAP (SBC-3) command. Version 2 adds anchor field
|
||||
* (sbc3r22). Otherwise same as sg_ll_unmap() . */
|
||||
int sg_ll_unmap_v2(int sg_fd, bool anchor, int group_num, int timeout_secs,
|
||||
void * paramp, int param_len, bool noisy, int verbose);
|
||||
|
||||
/* Invokes a SCSI VERIFY (10) command (SBC and MMC).
|
||||
* Note that 'veri_len' is in blocks while 'data_out_len' is in bytes.
|
||||
* Returns of 0 -> success,
|
||||
* SG_LIB_CAT_INVALID_OP -> Verify(10) not supported,
|
||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
|
||||
* SG_LIB_CAT_MEDIUM_HARD -> medium or hardware error, no valid info,
|
||||
* SG_LIB_CAT_MEDIUM_HARD_WITH_INFO -> as previous, with valid info,
|
||||
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
|
||||
* SG_LIB_CAT_MISCOMPARE, -1 -> other failure */
|
||||
int sg_ll_verify10(int sg_fd, int vrprotect, bool dpo, int bytechk,
|
||||
unsigned int lba, int veri_len, void * data_out,
|
||||
int data_out_len, unsigned int * infop, bool noisy,
|
||||
int verbose);
|
||||
|
||||
/* Invokes a SCSI VERIFY (16) command (SBC).
|
||||
* Note that 'veri_len' is in blocks while 'data_out_len' is in bytes.
|
||||
* Returns of 0 -> success,
|
||||
* SG_LIB_CAT_INVALID_OP -> Verify(16) not supported,
|
||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
|
||||
* SG_LIB_CAT_MEDIUM_HARD -> medium or hardware error, no valid info,
|
||||
* SG_LIB_CAT_MEDIUM_HARD_WITH_INFO -> as previous, with valid info,
|
||||
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
|
||||
* SG_LIB_CAT_MISCOMPARE, -1 -> other failure */
|
||||
int sg_ll_verify16(int sg_fd, int vrprotect, bool dpo, int bytechk,
|
||||
uint64_t llba, int veri_len, int group_num,
|
||||
void * data_out, int data_out_len, uint64_t * infop,
|
||||
bool noisy, int verbose);
|
||||
|
||||
/* Invokes a SCSI WRITE BUFFER command (SPC). Return of 0 ->
|
||||
* success, SG_LIB_CAT_INVALID_OP -> invalid opcode,
|
||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
|
||||
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
|
||||
* -1 -> other failure */
|
||||
int sg_ll_write_buffer(int sg_fd, int mode, int buffer_id, int buffer_offset,
|
||||
void * paramp, int param_len, bool noisy, int verbose);
|
||||
|
||||
/* Invokes a SCSI WRITE BUFFER command (SPC). Return of 0 ->
|
||||
* success, SG_LIB_CAT_INVALID_OP -> invalid opcode,
|
||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
|
||||
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
|
||||
* -1 -> other failure. Adds mode specific field (spc4r32) and timeout
|
||||
* to command abort to override default of 60 seconds. If timeout_secs is
|
||||
* 0 or less then the default timeout is used instead. */
|
||||
int
|
||||
sg_ll_write_buffer_v2(int sg_fd, int mode, int m_specific, int buffer_id,
|
||||
uint32_t buffer_offset, void * paramp,
|
||||
uint32_t param_len, int timeout_secs, bool noisy,
|
||||
int verbose);
|
||||
|
||||
/* Invokes a SCSI WRITE LONG (10) command (SBC). Note that 'xfer_len'
|
||||
* is in bytes. Returns 0 -> success,
|
||||
* SG_LIB_CAT_INVALID_OP -> WRITE LONG(10) not supported,
|
||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb,
|
||||
* SG_LIB_CAT_ILLEGAL_REQ_WITH_INFO -> bad field in cdb, with info
|
||||
* field written to 'offsetp', SG_LIB_CAT_UNIT_ATTENTION,
|
||||
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
|
||||
* -1 -> other failure */
|
||||
int sg_ll_write_long10(int sg_fd, bool cor_dis, bool wr_uncor, bool pblock,
|
||||
unsigned int lba, void * data_out, int xfer_len,
|
||||
int * offsetp, bool noisy, int verbose);
|
||||
|
||||
/* Invokes a SCSI WRITE LONG (16) command (SBC). Note that 'xfer_len'
|
||||
* is in bytes. Returns 0 -> success,
|
||||
* SG_LIB_CAT_INVALID_OP -> WRITE LONG(16) not supported,
|
||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb,
|
||||
* SG_LIB_CAT_ILLEGAL_REQ_WITH_INFO -> bad field in cdb, with info
|
||||
* field written to 'offsetp', SG_LIB_CAT_UNIT_ATTENTION,
|
||||
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
|
||||
* -1 -> other failure */
|
||||
int sg_ll_write_long16(int sg_fd, bool cor_dis, bool wr_uncor, bool pblock,
|
||||
uint64_t llba, void * data_out, int xfer_len,
|
||||
int * offsetp, bool noisy, int verbose);
|
||||
|
||||
/* Invokes a SPC-3 SCSI RECEIVE COPY RESULTS command. In SPC-4 this function
|
||||
* supports all service action variants of the THIRD-PARTY COPY IN opcode.
|
||||
* SG_LIB_CAT_INVALID_OP -> Receive copy results not supported,
|
||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
|
||||
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
|
||||
* -1 -> other failure */
|
||||
int sg_ll_receive_copy_results(int sg_fd, int sa, int list_id, void * resp,
|
||||
int mx_resp_len, bool noisy, int verbose);
|
||||
|
||||
/* Invokes a SCSI EXTENDED COPY(LID1) command. For EXTENDED COPY(LID4)
|
||||
* including POPULATE TOKEN and WRITE USING TOKEN use
|
||||
* sg_ll_3party_copy_out(). Return of 0 -> success,
|
||||
* SG_LIB_CAT_INVALID_OP -> Extended copy not supported,
|
||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
|
||||
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
|
||||
* -1 -> other failure */
|
||||
int sg_ll_extended_copy(int sg_fd, void * paramp, int param_len, bool noisy,
|
||||
int verbose);
|
||||
|
||||
/* Handles various service actions associated with opcode 0x83 which is
|
||||
* called THIRD PARTY COPY OUT. These include the EXTENDED COPY(LID4),
|
||||
* POPULATE TOKEN and WRITE USING TOKEN commands. Return of 0 -> success,
|
||||
* SG_LIB_CAT_INVALID_OP -> opcode 0x83 not supported,
|
||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
|
||||
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
|
||||
* -1 -> other failure */
|
||||
int sg_ll_3party_copy_out(int sg_fd, int sa, unsigned int list_id,
|
||||
int group_num, int timeout_secs, void * paramp,
|
||||
int param_len, bool noisy, int verbose);
|
||||
|
||||
/* Invokes a SCSI PRE-FETCH(10), PRE-FETCH(16) or SEEK(10) command (SBC).
|
||||
* Returns 0 -> success, 25 (SG_LIB_CAT_CONDITION_MET), various SG_LIB_CAT_*
|
||||
* positive values or -1 -> other errors. Note that CONDITION MET status
|
||||
* is returned when immed=true and num_blocks can fit in device's cache,
|
||||
* somewaht strangely, GOOD status (return 0) is returned if num_blocks
|
||||
* cannot fit in device's cache. If do_seek10==true then does a SEEK(10)
|
||||
* command with given lba, if that LBA is < 2**32 . Unclear what SEEK(10)
|
||||
* does, assume it is like PRE-FETCH. If timeout_secs is 0 (or less) then
|
||||
* use DEF_PT_TIMEOUT (60 seconds) as command timeout. */
|
||||
int sg_ll_pre_fetch_x(int sg_fd, bool do_seek10, bool cdb16, bool immed,
|
||||
uint64_t lba, uint32_t num_blocks, int group_num,
|
||||
int timeout_secs, bool noisy, int verbose);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
@ -1,52 +0,0 @@
|
||||
#ifndef SG_CMDS_MMC_H
|
||||
#define SG_CMDS_MMC_H
|
||||
|
||||
/*
|
||||
* Copyright (c) 2008-2017 Douglas Gilbert.
|
||||
* All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style
|
||||
* license that can be found in the BSD_LICENSE file.
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/* Invokes a SCSI GET CONFIGURATION command (MMC-3...6).
|
||||
* Returns 0 when successful, SG_LIB_CAT_INVALID_OP if command not
|
||||
* supported, SG_LIB_CAT_ILLEGAL_REQ if field in cdb not supported,
|
||||
* SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND, else -1 */
|
||||
int sg_ll_get_config(int sg_fd, int rt, int starting, void * resp,
|
||||
int mx_resp_len, bool noisy, int verbose);
|
||||
|
||||
/* Invokes a SCSI GET PERFORMANCE command (MMC-3...6).
|
||||
* Returns 0 when successful, SG_LIB_CAT_INVALID_OP if command not
|
||||
* supported, SG_LIB_CAT_ILLEGAL_REQ if field in cdb not supported,
|
||||
* SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND, else -1 */
|
||||
int sg_ll_get_performance(int sg_fd, int data_type, unsigned int starting_lba,
|
||||
int max_num_desc, int type, void * resp,
|
||||
int mx_resp_len, bool noisy, int verbose);
|
||||
|
||||
/* Invokes a SCSI SET CD SPEED command (MMC).
|
||||
* Return of 0 -> success, SG_LIB_CAT_INVALID_OP -> command not supported,
|
||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
|
||||
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
|
||||
* -1 -> other failure */
|
||||
int sg_ll_set_cd_speed(int sg_fd, int rot_control, int drv_read_speed,
|
||||
int drv_write_speed, bool noisy, int verbose);
|
||||
|
||||
/* Invokes a SCSI SET STREAMING command (MMC). Return of 0 -> success,
|
||||
* SG_LIB_CAT_INVALID_OP -> Set Streaming not supported,
|
||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND,
|
||||
* SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_NOT_READY -> device not ready,
|
||||
* -1 -> other failure */
|
||||
int sg_ll_set_streaming(int sg_fd, int type, void * paramp, int param_len,
|
||||
bool noisy, int verbose);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
@ -1,185 +0,0 @@
|
||||
#ifndef SG_IO_LINUX_H
|
||||
#define SG_IO_LINUX_H
|
||||
|
||||
/*
|
||||
* Copyright (c) 2004-2017 Douglas Gilbert.
|
||||
* All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style
|
||||
* license that can be found in the BSD_LICENSE file.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Version 1.05 [20171009]
|
||||
*/
|
||||
|
||||
/*
|
||||
* This header file contains linux specific information related to the SCSI
|
||||
* command pass through in the SCSI generic (sg) driver and the linux
|
||||
* block layer.
|
||||
*/
|
||||
|
||||
#include "sg_lib.h"
|
||||
#include "sg_linux_inc.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* The following are 'host_status' codes */
|
||||
#ifndef DID_OK
|
||||
#define DID_OK 0x00
|
||||
#endif
|
||||
#ifndef DID_NO_CONNECT
|
||||
#define DID_NO_CONNECT 0x01 /* Unable to connect before timeout */
|
||||
#define DID_BUS_BUSY 0x02 /* Bus remain busy until timeout */
|
||||
#define DID_TIME_OUT 0x03 /* Timed out for some other reason */
|
||||
#define DID_BAD_TARGET 0x04 /* Bad target (id?) */
|
||||
#define DID_ABORT 0x05 /* Told to abort for some other reason */
|
||||
#define DID_PARITY 0x06 /* Parity error (on SCSI bus) */
|
||||
#define DID_ERROR 0x07 /* Internal error */
|
||||
#define DID_RESET 0x08 /* Reset by somebody */
|
||||
#define DID_BAD_INTR 0x09 /* Received an unexpected interrupt */
|
||||
#define DID_PASSTHROUGH 0x0a /* Force command past mid-level */
|
||||
#define DID_SOFT_ERROR 0x0b /* The low-level driver wants a retry */
|
||||
#endif
|
||||
#ifndef DID_IMM_RETRY
|
||||
#define DID_IMM_RETRY 0x0c /* Retry without decrementing retry count */
|
||||
#endif
|
||||
#ifndef DID_REQUEUE
|
||||
#define DID_REQUEUE 0x0d /* Requeue command (no immediate retry) also
|
||||
* without decrementing the retry count */
|
||||
#endif
|
||||
#ifndef DID_TRANSPORT_DISRUPTED
|
||||
#define DID_TRANSPORT_DISRUPTED 0xe
|
||||
#endif
|
||||
#ifndef DID_TRANSPORT_FAILFAST
|
||||
#define DID_TRANSPORT_FAILFAST 0xf
|
||||
#endif
|
||||
#ifndef DID_TARGET_FAILURE
|
||||
#define DID_TARGET_FAILURE 0x10
|
||||
#endif
|
||||
#ifndef DID_NEXUS_FAILURE
|
||||
#define DID_NEXUS_FAILURE 0x11
|
||||
#endif
|
||||
|
||||
/* These defines are to isolate applications from kernel define changes */
|
||||
#define SG_LIB_DID_OK DID_OK
|
||||
#define SG_LIB_DID_NO_CONNECT DID_NO_CONNECT
|
||||
#define SG_LIB_DID_BUS_BUSY DID_BUS_BUSY
|
||||
#define SG_LIB_DID_TIME_OUT DID_TIME_OUT
|
||||
#define SG_LIB_DID_BAD_TARGET DID_BAD_TARGET
|
||||
#define SG_LIB_DID_ABORT DID_ABORT
|
||||
#define SG_LIB_DID_PARITY DID_PARITY
|
||||
#define SG_LIB_DID_ERROR DID_ERROR
|
||||
#define SG_LIB_DID_RESET DID_RESET
|
||||
#define SG_LIB_DID_BAD_INTR DID_BAD_INTR
|
||||
#define SG_LIB_DID_PASSTHROUGH DID_PASSTHROUGH
|
||||
#define SG_LIB_DID_SOFT_ERROR DID_SOFT_ERROR
|
||||
#define SG_LIB_DID_IMM_RETRY DID_IMM_RETRY
|
||||
#define SG_LIB_DID_REQUEUE DID_REQUEUE
|
||||
#define SG_LIB_TRANSPORT_DISRUPTED DID_TRANSPORT_DISRUPTED
|
||||
#define SG_LIB_DID_TRANSPORT_FAILFAST DID_TRANSPORT_FAILFAST
|
||||
#define SG_LIB_DID_TARGET_FAILURE DID_TARGET_FAILURE
|
||||
#define SG_LIB_DID_NEXUS_FAILURE DID_NEXUS_FAILURE
|
||||
|
||||
/* The following are 'driver_status' codes */
|
||||
#ifndef DRIVER_OK
|
||||
#define DRIVER_OK 0x00
|
||||
#endif
|
||||
#ifndef DRIVER_BUSY
|
||||
#define DRIVER_BUSY 0x01
|
||||
#define DRIVER_SOFT 0x02
|
||||
#define DRIVER_MEDIA 0x03
|
||||
#define DRIVER_ERROR 0x04
|
||||
#define DRIVER_INVALID 0x05
|
||||
#define DRIVER_TIMEOUT 0x06
|
||||
#define DRIVER_HARD 0x07
|
||||
#define DRIVER_SENSE 0x08 /* Sense_buffer has been set */
|
||||
|
||||
/* N.B. the SUGGEST_* codes are no longer used in Linux and are only kept
|
||||
* to stop compilation breakages.
|
||||
* Following "suggests" are "or-ed" with one of previous 8 entries */
|
||||
#define SUGGEST_RETRY 0x10
|
||||
#define SUGGEST_ABORT 0x20
|
||||
#define SUGGEST_REMAP 0x30
|
||||
#define SUGGEST_DIE 0x40
|
||||
#define SUGGEST_SENSE 0x80
|
||||
#define SUGGEST_IS_OK 0xff
|
||||
#endif
|
||||
|
||||
#ifndef DRIVER_MASK
|
||||
#define DRIVER_MASK 0x0f
|
||||
#endif
|
||||
#ifndef SUGGEST_MASK
|
||||
#define SUGGEST_MASK 0xf0
|
||||
#endif
|
||||
|
||||
/* These defines are to isolate applications from kernel define changes */
|
||||
#define SG_LIB_DRIVER_OK DRIVER_OK
|
||||
#define SG_LIB_DRIVER_BUSY DRIVER_BUSY
|
||||
#define SG_LIB_DRIVER_SOFT DRIVER_SOFT
|
||||
#define SG_LIB_DRIVER_MEDIA DRIVER_MEDIA
|
||||
#define SG_LIB_DRIVER_ERROR DRIVER_ERROR
|
||||
#define SG_LIB_DRIVER_INVALID DRIVER_INVALID
|
||||
#define SG_LIB_DRIVER_TIMEOUT DRIVER_TIMEOUT
|
||||
#define SG_LIB_DRIVER_HARD DRIVER_HARD
|
||||
#define SG_LIB_DRIVER_SENSE DRIVER_SENSE
|
||||
|
||||
|
||||
/* N.B. the SUGGEST_* codes are no longer used in Linux and are only kept
|
||||
* to stop compilation breakages. */
|
||||
#define SG_LIB_SUGGEST_RETRY SUGGEST_RETRY
|
||||
#define SG_LIB_SUGGEST_ABORT SUGGEST_ABORT
|
||||
#define SG_LIB_SUGGEST_REMAP SUGGEST_REMAP
|
||||
#define SG_LIB_SUGGEST_DIE SUGGEST_DIE
|
||||
#define SG_LIB_SUGGEST_SENSE SUGGEST_SENSE
|
||||
#define SG_LIB_SUGGEST_IS_OK SUGGEST_IS_OK
|
||||
#define SG_LIB_DRIVER_MASK DRIVER_MASK
|
||||
#define SG_LIB_SUGGEST_MASK SUGGEST_MASK
|
||||
|
||||
void sg_print_masked_status(int masked_status);
|
||||
void sg_print_host_status(int host_status);
|
||||
void sg_print_driver_status(int driver_status);
|
||||
|
||||
/* sg_chk_n_print() returns 1 quietly if there are no errors/warnings
|
||||
else it prints errors/warnings (prefixed by 'leadin') to
|
||||
'sg_warnings_fd' and returns 0. raw_sinfo indicates whether the
|
||||
raw sense buffer (in ASCII hex) should be printed. */
|
||||
int sg_chk_n_print(const char * leadin, int masked_status, int host_status,
|
||||
int driver_status, const unsigned char * sense_buffer,
|
||||
int sb_len, bool raw_sinfo);
|
||||
|
||||
/* The following function declaration is for the sg version 3 driver. */
|
||||
struct sg_io_hdr;
|
||||
/* sg_chk_n_print3() returns 1 quietly if there are no errors/warnings;
|
||||
else it prints errors/warnings (prefixed by 'leadin') to
|
||||
'sg_warnings_fd' and returns 0. */
|
||||
int sg_chk_n_print3(const char * leadin, struct sg_io_hdr * hp,
|
||||
bool raw_sinfo);
|
||||
|
||||
/* Calls sg_scsi_normalize_sense() after obtaining the sense buffer and
|
||||
its length from the struct sg_io_hdr pointer. If these cannot be
|
||||
obtained, false is returned. */
|
||||
bool sg_normalize_sense(const struct sg_io_hdr * hp,
|
||||
struct sg_scsi_sense_hdr * sshp);
|
||||
|
||||
int sg_err_category(int masked_status, int host_status, int driver_status,
|
||||
const unsigned char * sense_buffer, int sb_len);
|
||||
|
||||
int sg_err_category_new(int scsi_status, int host_status, int driver_status,
|
||||
const unsigned char * sense_buffer, int sb_len);
|
||||
|
||||
/* The following function declaration is for the sg version 3 driver. */
|
||||
int sg_err_category3(struct sg_io_hdr * hp);
|
||||
|
||||
|
||||
/* Note about SCSI status codes found in older versions of Linux.
|
||||
Linux has traditionally used a 1 bit right shifted and masked
|
||||
version of SCSI standard status codes. Now CHECK_CONDITION
|
||||
and friends (in <scsi/scsi.h>) are deprecated. */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
@ -1,602 +0,0 @@
|
||||
#ifndef SG_LIB_H
|
||||
#define SG_LIB_H
|
||||
|
||||
/*
|
||||
* Copyright (c) 2004-2018 Douglas Gilbert.
|
||||
* All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style
|
||||
* license that can be found in the BSD_LICENSE file.
|
||||
*/
|
||||
|
||||
/*
|
||||
*
|
||||
* On 5th October 2004 a FreeBSD license was added to this file.
|
||||
* The intention is to keep this file and the related sg_lib.c file
|
||||
* as open source and encourage their unencumbered use.
|
||||
*
|
||||
* Current version number is in the sg_lib.c file and can be accessed
|
||||
* with the sg_lib_version() function.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* This header file contains defines and function declarations that may
|
||||
* be useful to applications that communicate with devices that use a
|
||||
* SCSI command set. These command sets have names like SPC-4, SBC-3,
|
||||
* SSC-3, SES-2 and draft standards defining them can be found at
|
||||
* http://www.t10.org . Virtually all devices in the Linux SCSI subsystem
|
||||
* utilize SCSI command sets. Many devices in other Linux device subsystems
|
||||
* utilize SCSI command sets either natively or via emulation (e.g. a
|
||||
* parallel ATA disk in a USB enclosure).
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* SCSI Peripheral Device Types (PDT) [5 bit field] */
|
||||
#define PDT_DISK 0x0 /* direct access block device (disk) */
|
||||
#define PDT_TAPE 0x1 /* sequential access device (magnetic tape) */
|
||||
#define PDT_PRINTER 0x2 /* printer device (see SSC-1) */
|
||||
#define PDT_PROCESSOR 0x3 /* processor device (e.g. SAFTE device) */
|
||||
#define PDT_WO 0x4 /* write once device (some optical disks) */
|
||||
#define PDT_MMC 0x5 /* CD/DVD/BD (multi-media) */
|
||||
#define PDT_SCANNER 0x6 /* obsolete */
|
||||
#define PDT_OPTICAL 0x7 /* optical memory device (some optical disks) */
|
||||
#define PDT_MCHANGER 0x8 /* media changer device (e.g. tape robot) */
|
||||
#define PDT_COMMS 0x9 /* communications device (obsolete) */
|
||||
#define PDT_SAC 0xc /* storage array controller device */
|
||||
#define PDT_SES 0xd /* SCSI Enclosure Services (SES) device */
|
||||
#define PDT_RBC 0xe /* Reduced Block Commands (simplified PDT_DISK) */
|
||||
#define PDT_OCRW 0xf /* optical card read/write device */
|
||||
#define PDT_BCC 0x10 /* bridge controller commands */
|
||||
#define PDT_OSD 0x11 /* Object Storage Device (OSD) */
|
||||
#define PDT_ADC 0x12 /* Automation/drive commands (ADC) */
|
||||
#define PDT_SMD 0x13 /* Security Manager Device (SMD) */
|
||||
#define PDT_ZBC 0x14 /* Zoned Block Commands (ZBC) */
|
||||
#define PDT_WLUN 0x1e /* Well known logical unit (WLUN) */
|
||||
#define PDT_UNKNOWN 0x1f /* Unknown or no device type */
|
||||
|
||||
#ifndef SAM_STAT_GOOD
|
||||
/* The SCSI status codes as found in SAM-4 at www.t10.org */
|
||||
#define SAM_STAT_GOOD 0x0
|
||||
#define SAM_STAT_CHECK_CONDITION 0x2
|
||||
#define SAM_STAT_CONDITION_MET 0x4
|
||||
#define SAM_STAT_BUSY 0x8
|
||||
#define SAM_STAT_INTERMEDIATE 0x10 /* obsolete in SAM-4 */
|
||||
#define SAM_STAT_INTERMEDIATE_CONDITION_MET 0x14 /* obsolete in SAM-4 */
|
||||
#define SAM_STAT_RESERVATION_CONFLICT 0x18
|
||||
#define SAM_STAT_COMMAND_TERMINATED 0x22 /* obsolete in SAM-3 */
|
||||
#define SAM_STAT_TASK_SET_FULL 0x28
|
||||
#define SAM_STAT_ACA_ACTIVE 0x30
|
||||
#define SAM_STAT_TASK_ABORTED 0x40
|
||||
#endif
|
||||
|
||||
/* The SCSI sense key codes as found in SPC-4 at www.t10.org */
|
||||
#define SPC_SK_NO_SENSE 0x0
|
||||
#define SPC_SK_RECOVERED_ERROR 0x1
|
||||
#define SPC_SK_NOT_READY 0x2
|
||||
#define SPC_SK_MEDIUM_ERROR 0x3
|
||||
#define SPC_SK_HARDWARE_ERROR 0x4
|
||||
#define SPC_SK_ILLEGAL_REQUEST 0x5
|
||||
#define SPC_SK_UNIT_ATTENTION 0x6
|
||||
#define SPC_SK_DATA_PROTECT 0x7
|
||||
#define SPC_SK_BLANK_CHECK 0x8
|
||||
#define SPC_SK_VENDOR_SPECIFIC 0x9
|
||||
#define SPC_SK_COPY_ABORTED 0xa
|
||||
#define SPC_SK_ABORTED_COMMAND 0xb
|
||||
#define SPC_SK_RESERVED 0xc
|
||||
#define SPC_SK_VOLUME_OVERFLOW 0xd
|
||||
#define SPC_SK_MISCOMPARE 0xe
|
||||
#define SPC_SK_COMPLETED 0xf
|
||||
|
||||
/* Transport protocol identifiers or just Protocol identifiers */
|
||||
#define TPROTO_FCP 0
|
||||
#define TPROTO_SPI 1
|
||||
#define TPROTO_SSA 2
|
||||
#define TPROTO_1394 3
|
||||
#define TPROTO_SRP 4 /* SCSI over RDMA */
|
||||
#define TPROTO_ISCSI 5
|
||||
#define TPROTO_SAS 6
|
||||
#define TPROTO_ADT 7
|
||||
#define TPROTO_ATA 8
|
||||
#define TPROTO_UAS 9 /* USB attached SCSI */
|
||||
#define TPROTO_SOP 0xa /* SCSI over PCIe */
|
||||
#define TPROTO_PCIE 0xb /* includes NVMe */
|
||||
#define TPROTO_NONE 0xf
|
||||
|
||||
/* SCSI Feature Sets (sfs) */
|
||||
#define SCSI_FS_SPC_DISCOVERY_2016 0x1
|
||||
#define SCSI_FS_SBC_BASE_2010 0x102
|
||||
#define SCSI_FS_SBC_BASE_2016 0x101
|
||||
#define SCSI_FS_SBC_BASIC_PROV_2016 0x103
|
||||
#define SCSI_FS_SBC_DRIVE_MAINT_2016 0x104
|
||||
|
||||
/* Often SCSI responses use the highest integer that can fit in a field
|
||||
* to indicate "unbounded" or limit does not apply. Sometimes represented
|
||||
* in output as "-1" for brevity */
|
||||
#define SG_LIB_UNBOUNDED_16BIT 0xffff
|
||||
#define SG_LIB_UNBOUNDED_32BIT 0xffffffffU
|
||||
#define SG_LIB_UNBOUNDED_64BIT 0xffffffffffffffffULL
|
||||
|
||||
#if (__STDC_VERSION__ >= 199901L) /* C99 or later */
|
||||
typedef uintptr_t sg_uintptr_t;
|
||||
#else
|
||||
typedef unsigned long sg_uintptr_t;
|
||||
#endif
|
||||
|
||||
|
||||
/* The format of the version string is like this: "2.26 20170906" */
|
||||
const char * sg_lib_version();
|
||||
|
||||
/* Returns length of SCSI command given the opcode (first byte).
|
||||
* Yields the wrong answer for variable length commands (opcode=0x7f)
|
||||
* and potentially some vendor specific commands. */
|
||||
int sg_get_command_size(unsigned char cdb_byte0);
|
||||
|
||||
/* Command name given pointer to the cdb. Certain command names
|
||||
* depend on peripheral type (give 0 or -1 if unknown). Places command
|
||||
* name into buff and will write no more than buff_len bytes. */
|
||||
void sg_get_command_name(const unsigned char * cdbp, int peri_type,
|
||||
int buff_len, char * buff);
|
||||
|
||||
/* Command name given only the first byte (byte 0) of a cdb and
|
||||
* peripheral type (give 0 or -1 if unknown). */
|
||||
void sg_get_opcode_name(unsigned char cdb_byte0, int peri_type, int buff_len,
|
||||
char * buff);
|
||||
|
||||
/* Command name given opcode (byte 0), service action and peripheral type.
|
||||
* If no service action give 0, if unknown peripheral type give 0 or -1 . */
|
||||
void sg_get_opcode_sa_name(unsigned char cdb_byte0, int service_action,
|
||||
int peri_type, int buff_len, char * buff);
|
||||
|
||||
/* Fetch scsi status string. */
|
||||
void sg_get_scsi_status_str(int scsi_status, int buff_len, char * buff);
|
||||
|
||||
/* This is a slightly stretched SCSI sense "descriptor" format header.
|
||||
* The addition is to allow the 0x70 and 0x71 response codes. The idea
|
||||
* is to place the salient data of both "fixed" and "descriptor" sense
|
||||
* format into one structure to ease application processing.
|
||||
* The original sense buffer should be kept around for those cases
|
||||
* in which more information is required (e.g. the LBA of a MEDIUM ERROR). */
|
||||
struct sg_scsi_sense_hdr {
|
||||
unsigned char response_code; /* permit: 0x0, 0x70, 0x71, 0x72, 0x73 */
|
||||
unsigned char sense_key;
|
||||
unsigned char asc;
|
||||
unsigned char ascq;
|
||||
unsigned char byte4;
|
||||
unsigned char byte5;
|
||||
unsigned char byte6;
|
||||
unsigned char additional_length;
|
||||
};
|
||||
|
||||
/* Maps the salient data from a sense buffer which is in either fixed or
|
||||
* descriptor format into a structure mimicking a descriptor format
|
||||
* header (i.e. the first 8 bytes of sense descriptor format).
|
||||
* If zero response code returns false. Otherwise returns true and if 'sshp'
|
||||
* is non-NULL then zero all fields and then set the appropriate fields in
|
||||
* that structure. sshp::additional_length is always 0 for response
|
||||
* codes 0x70 and 0x71 (fixed format). */
|
||||
bool sg_scsi_normalize_sense(const unsigned char * sensep, int sense_len,
|
||||
struct sg_scsi_sense_hdr * sshp);
|
||||
|
||||
/* Attempt to find the first SCSI sense data descriptor that matches the
|
||||
* given 'desc_type'. If found return pointer to start of sense data
|
||||
* descriptor; otherwise (including fixed format sense data) returns NULL. */
|
||||
const unsigned char * sg_scsi_sense_desc_find(const unsigned char * sensep,
|
||||
int sense_len, int desc_type);
|
||||
|
||||
/* Get sense key from sense buffer. If successful returns a sense key value
|
||||
* between 0 and 15. If sense buffer cannot be decode, returns -1 . */
|
||||
int sg_get_sense_key(const unsigned char * sensep, int sense_len);
|
||||
|
||||
/* Yield string associated with sense_key value. Returns 'buff'. */
|
||||
char * sg_get_sense_key_str(int sense_key, int buff_len, char * buff);
|
||||
|
||||
/* Yield string associated with ASC/ASCQ values. Returns 'buff'. */
|
||||
char * sg_get_asc_ascq_str(int asc, int ascq, int buff_len, char * buff);
|
||||
|
||||
/* Returns true if valid bit set, false if valid bit clear. Irrespective the
|
||||
* information field is written out via 'info_outp' (except when it is
|
||||
* NULL). Handles both fixed and descriptor sense formats. */
|
||||
bool sg_get_sense_info_fld(const unsigned char * sensep, int sb_len,
|
||||
uint64_t * info_outp);
|
||||
|
||||
/* Returns true if fixed format or command specific information descriptor
|
||||
* is found in the descriptor sense; else false. If available the command
|
||||
* specific information field (4 byte integer in fixed format, 8 byte
|
||||
* integer in descriptor format) is written out via 'cmd_spec_outp'.
|
||||
* Handles both fixed and descriptor sense formats. */
|
||||
bool sg_get_sense_cmd_spec_fld(const unsigned char * sensep, int sb_len,
|
||||
uint64_t * cmd_spec_outp);
|
||||
|
||||
/* Returns true if any of the 3 bits (i.e. FILEMARK, EOM or ILI) are set.
|
||||
* In descriptor format if the stream commands descriptor not found
|
||||
* then returns false. Writes true or false corresponding to these bits to
|
||||
* the last three arguments if they are non-NULL. */
|
||||
bool sg_get_sense_filemark_eom_ili(const unsigned char * sensep, int sb_len,
|
||||
bool * filemark_p, bool * eom_p,
|
||||
bool * ili_p);
|
||||
|
||||
/* Returns true if SKSV is set and sense key is NO_SENSE or NOT_READY. Also
|
||||
* returns true if progress indication sense data descriptor found. Places
|
||||
* progress field from sense data where progress_outp points. If progress
|
||||
* field is not available returns false. Handles both fixed and descriptor
|
||||
* sense formats. N.B. App should multiply by 100 and divide by 65536
|
||||
* to get percentage completion from given value. */
|
||||
bool sg_get_sense_progress_fld(const unsigned char * sensep, int sb_len,
|
||||
int * progress_outp);
|
||||
|
||||
/* Closely related to sg_print_sense(). Puts decoded sense data in 'buff'.
|
||||
* Usually multiline with multiple '\n' including one trailing. If
|
||||
* 'raw_sinfo' set appends sense buffer in hex. 'leadin' is string prepended
|
||||
* to each line written to 'buff', NULL treated as "". Returns the number of
|
||||
* bytes written to 'buff' excluding the trailing '\0'.
|
||||
* N.B. prior to sg3_utils v 1.42 'leadin' was only prepended to the first
|
||||
* line output. Also this function returned type void. */
|
||||
int sg_get_sense_str(const char * leadin, const unsigned char * sense_buffer,
|
||||
int sb_len, bool raw_sinfo, int buff_len, char * buff);
|
||||
|
||||
/* Decode descriptor format sense descriptors (assumes sense buffer is
|
||||
* in descriptor format). 'leadin' is string prepended to each line written
|
||||
* to 'b', NULL treated as "". Returns the number of bytes written to 'b'
|
||||
* excluding the trailing '\0'. */
|
||||
int sg_get_sense_descriptors_str(const char * leadin,
|
||||
const unsigned char * sense_buffer,
|
||||
int sb_len, int blen, char * b);
|
||||
|
||||
/* Decodes a designation descriptor (e.g. as found in the Device
|
||||
* Identification VPD page (0x83)) into string 'b' whose maximum length is
|
||||
* blen. 'leadin' is string prepended to each line written to 'b', NULL
|
||||
* treated as "". Returns the number of bytes written to 'b' excluding the
|
||||
* trailing '\0'. */
|
||||
int sg_get_designation_descriptor_str(const char * leadin,
|
||||
const unsigned char * ddp, int dd_len,
|
||||
bool print_assoc, bool do_long,
|
||||
int blen, char * b);
|
||||
|
||||
/* Yield string associated with peripheral device type (pdt). Returns
|
||||
* 'buff'. If 'pdt' out of range yields "bad pdt" string. */
|
||||
char * sg_get_pdt_str(int pdt, int buff_len, char * buff);
|
||||
|
||||
/* Some lesser used PDTs share a lot in common with a more used PDT.
|
||||
* Examples are PDT_ADC decaying to PDT_TAPE and PDT_ZBC to PDT_DISK.
|
||||
* If such a lesser used 'pdt' is given to this function, then it will
|
||||
* return the more used PDT (i.e. "decays to"); otherwise 'pdt' is returned.
|
||||
* Valid for 'pdt' 0 to 31, for other values returns 0. */
|
||||
int sg_lib_pdt_decay(int pdt);
|
||||
|
||||
/* Yield string associated with transport protocol identifier (tpi). Returns
|
||||
* 'buff'. If 'tpi' out of range yields "bad tpi" string. */
|
||||
char * sg_get_trans_proto_str(int tpi, int buff_len, char * buff);
|
||||
|
||||
/* Decode TransportID pointed to by 'bp' of length 'bplen'. Place decoded
|
||||
* string output in 'buff' which is also the return value. Each new line
|
||||
* is prefixed by 'leadin'. If leadin NULL treat as "". */
|
||||
char * sg_decode_transportid_str(const char * leadin, unsigned char * bp,
|
||||
int bplen, bool only_one, int buff_len,
|
||||
char * buff);
|
||||
|
||||
/* Returns a designator's type string given 'val' (0 to 15 inclusive),
|
||||
* otherwise returns NULL. */
|
||||
const char * sg_get_desig_type_str(int val);
|
||||
|
||||
/* Returns a designator's code_set string given 'val' (0 to 15 inclusive),
|
||||
* otherwise returns NULL. */
|
||||
const char * sg_get_desig_code_set_str(int val);
|
||||
|
||||
/* Returns a designator's association string given 'val' (0 to 3 inclusive),
|
||||
* otherwise returns NULL. */
|
||||
const char * sg_get_desig_assoc_str(int val);
|
||||
|
||||
/* Yield SCSI Feature Set (sfs) string. When 'peri_type' is < -1 (or > 31)
|
||||
* returns pointer to string (same as 'buff') associated with 'sfs_code'.
|
||||
* When 'peri_type' is between -1 (for SPC) and 31 (inclusive) then a match
|
||||
* on both 'sfs_code' and 'peri_type' is required. If 'foundp' is not NULL
|
||||
* then where it points is set to true if a match is found else it is set to
|
||||
* false. If 'buff' is not NULL then in the case of a match a descriptive
|
||||
* string is written to 'buff' while if there is not a not then a string
|
||||
* ending in "Reserved" is written (and may be prefixed with SPC, SBC, SSC
|
||||
* or ZBC). Returns 'buff' (i.e. a pointer value) even if it is NULL.
|
||||
* Example:
|
||||
* char b[64];
|
||||
* ...
|
||||
* printf("%s\n", sg_get_sfs_str(sfs_code, -2, sizeof(b), b, NULL, 0));
|
||||
*/
|
||||
const char * sg_get_sfs_str(uint16_t sfs_code, int peri_type, int buff_len,
|
||||
char * buff, bool * foundp, int verbose);
|
||||
|
||||
/* This is a heuristic that takes into account the command bytes and length
|
||||
* to decide whether the presented unstructured sequence of bytes could be
|
||||
* a SCSI command. If so it returns true otherwise false. Vendor specific
|
||||
* SCSI commands (i.e. opcodes from 0xc0 to 0xff), if presented, are assumed
|
||||
* to follow SCSI conventions (i.e. length of 6, 10, 12 or 16 bytes). The
|
||||
* only SCSI commands considered above 16 bytes of length are the Variable
|
||||
* Length Commands (opcode 0x7f) and the XCDB wrapped commands (opcode 0x7e).
|
||||
* Both have an inbuilt length field which can be cross checked with clen.
|
||||
* No NVMe commands (64 bytes long plus some extra added by some OSes) have
|
||||
* opcodes 0x7e or 0x7f yet. ATA is register based but SATA has FIS
|
||||
* structures that are sent across the wire. The 'FIS register' structure is
|
||||
* used to move a command from a SATA host to device, but the ATA 'command'
|
||||
* is not the first byte. So it is harder to say what will happen if a
|
||||
* FIS structure is presented as a SCSI command, hopfully there is a low
|
||||
* probability this function will yield true in that case. */
|
||||
bool sg_is_scsi_cdb(const uint8_t * cdbp, int clen);
|
||||
|
||||
/* Yield string associated with NVMe command status value in sct_sc. It
|
||||
* expects to decode DW3 bits 27:17 from the completion queue. Bits 27:25
|
||||
* are the Status Code Type (SCT) and bits 24:17 are the Status Code (SC).
|
||||
* Bit 17 in DW3 should be bit 0 in sct_sc. If no status string is found
|
||||
* a string of the form "Reserved [0x<sct_sc_in_hex>]" is generated.
|
||||
* Returns 'buff'. Does nothing if buff_len<=0 or if buff is NULL.*/
|
||||
char * sg_get_nvme_cmd_status_str(uint16_t sct_sc, int buff_len, char * buff);
|
||||
|
||||
/* Attempts to map NVMe status value ((SCT << 8) | SC) n sct_sc to a SCSI
|
||||
* status, sense_key, asc and ascq tuple. If successful returns true and
|
||||
* writes to non-NULL pointer arguments; otherwise returns false. */
|
||||
bool sg_nvme_status2scsi(uint16_t sct_sc, uint8_t * status_p, uint8_t * sk_p,
|
||||
uint8_t * asc_p, uint8_t * ascq_p);
|
||||
|
||||
extern FILE * sg_warnings_strm;
|
||||
|
||||
void sg_set_warnings_strm(FILE * warnings_strm);
|
||||
|
||||
/* The following "print" functions send ACSII to 'sg_warnings_strm' file
|
||||
* descriptor (default value is stderr). 'leadin' is string prepended to
|
||||
* each line printed out, NULL treated as "". */
|
||||
void sg_print_command(const unsigned char * command);
|
||||
void sg_print_scsi_status(int scsi_status);
|
||||
|
||||
/* 'leadin' is string prepended to each line printed out, NULL treated as
|
||||
* "". N.B. prior to sg3_utils v 1.42 'leadin' was only prepended to the
|
||||
* first line printed. */
|
||||
void sg_print_sense(const char * leadin, const unsigned char * sense_buffer,
|
||||
int sb_len, bool raw_info);
|
||||
|
||||
/* Following examines exit_status and outputs a clear error message to
|
||||
* warnings_strm (usually stderr) if one is known and returns true.
|
||||
* Otherwise it doesn't print anything and returns false. Note that if
|
||||
* exit_status==0 then returns true but prints nothing and if
|
||||
* exit_status<0 ("some error occurred") false is returned. If leadin is
|
||||
* non-NULL is will be printed before error message. */
|
||||
bool sg_if_can2stderr(const char * leadin, int exit_status);
|
||||
|
||||
/* Utilities can use these exit status values for syntax errors and
|
||||
* file (device node) problems (e.g. not found or permissions). */
|
||||
#define SG_LIB_SYNTAX_ERROR 1 /* command line syntax problem */
|
||||
#define SG_LIB_FILE_ERROR 15 /* device or other file problem */
|
||||
|
||||
/* The sg_err_category_sense() function returns one of the following.
|
||||
* These may be used as exit status values (from a process). Notice that
|
||||
* some of the lower values correspond to SCSI sense key values. */
|
||||
#define SG_LIB_CAT_CLEAN 0 /* No errors or other information */
|
||||
/* Value 1 left unused for utilities to use SG_LIB_SYNTAX_ERROR */
|
||||
#define SG_LIB_CAT_NOT_READY 2 /* sense key, unit stopped? */
|
||||
/* [sk,asc,ascq: 0x2,*,*] */
|
||||
#define SG_LIB_CAT_MEDIUM_HARD 3 /* medium or hardware error, blank check */
|
||||
/* [sk,asc,ascq: 0x3/0x4/0x8,*,*] */
|
||||
#define SG_LIB_CAT_ILLEGAL_REQ 5 /* Illegal request (other than invalid */
|
||||
/* opcode): [sk,asc,ascq: 0x5,*,*] */
|
||||
#define SG_LIB_CAT_UNIT_ATTENTION 6 /* sense key, device state changed */
|
||||
/* [sk,asc,ascq: 0x6,*,*] */
|
||||
/* was SG_LIB_CAT_MEDIA_CHANGED earlier [sk,asc,ascq: 0x6,0x28,*] */
|
||||
#define SG_LIB_CAT_DATA_PROTECT 7 /* sense key, media write protected? */
|
||||
/* [sk,asc,ascq: 0x7,*,*] */
|
||||
#define SG_LIB_CAT_INVALID_OP 9 /* (Illegal request,) Invalid opcode: */
|
||||
/* [sk,asc,ascq: 0x5,0x20,0x0] */
|
||||
#define SG_LIB_CAT_COPY_ABORTED 10 /* sense key, some data transferred */
|
||||
/* [sk,asc,ascq: 0xa,*,*] */
|
||||
#define SG_LIB_CAT_ABORTED_COMMAND 11 /* interpreted from sense buffer */
|
||||
/* [sk,asc,ascq: 0xb,! 0x10,*] */
|
||||
#define SG_LIB_CAT_MISCOMPARE 14 /* sense key, probably verify */
|
||||
/* [sk,asc,ascq: 0xe,*,*] */
|
||||
#define SG_LIB_CAT_NO_SENSE 20 /* sense data with key of "no sense" */
|
||||
/* [sk,asc,ascq: 0x0,*,*] */
|
||||
#define SG_LIB_CAT_RECOVERED 21 /* Successful command after recovered err */
|
||||
/* [sk,asc,ascq: 0x1,*,*] */
|
||||
#define SG_LIB_CAT_RES_CONFLICT SAM_STAT_RESERVATION_CONFLICT
|
||||
/* 24: this is a SCSI status, not sense. */
|
||||
/* It indicates reservation by another */
|
||||
/* machine blocks this command */
|
||||
#define SG_LIB_CAT_CONDITION_MET 25 /* SCSI status, not sense key. */
|
||||
/* Only from PRE-FETCH (SBC-4) */
|
||||
#define SG_LIB_CAT_BUSY 26 /* SCSI status, not sense. Invites retry */
|
||||
#define SG_LIB_CAT_TS_FULL 27 /* SCSI status, not sense. Wait then retry */
|
||||
#define SG_LIB_CAT_ACA_ACTIVE 28 /* SCSI status; ACA seldom used */
|
||||
#define SG_LIB_CAT_TASK_ABORTED 29 /* SCSI status, this command aborted by? */
|
||||
#define SG_LIB_CAT_PROTECTION 40 /* subset of aborted command (for PI, DIF) */
|
||||
/* [sk,asc,ascq: 0xb,0x10,*] */
|
||||
#define SG_LIB_NVME_STATUS 48 /* NVMe Status Field (SF) other than 0 */
|
||||
#define SG_LIB_WILD_RESID 49 /* Residual value for data-in transfer of a */
|
||||
/* SCSI command is nonsensical */
|
||||
#define SG_LIB_OS_BASE_ERR 50 /* in Linux: values found in: */
|
||||
/* include/uapi/asm-generic/errno-base.h */
|
||||
/* Example: ENOMEM reported as 62 (=50+12) */
|
||||
#define SG_LIB_CAT_MALFORMED 97 /* Response to SCSI command malformed */
|
||||
#define SG_LIB_CAT_SENSE 98 /* Something else is in the sense buffer */
|
||||
#define SG_LIB_CAT_OTHER 99 /* Some other error/warning has occurred */
|
||||
/* (e.g. a transport or driver error) */
|
||||
|
||||
/* Returns a SG_LIB_CAT_* value. If cannot decode sense_buffer or a less
|
||||
* common sense key then return SG_LIB_CAT_SENSE .*/
|
||||
int sg_err_category_sense(const unsigned char * sense_buffer, int sb_len);
|
||||
|
||||
/* Here are some additional sense data categories that are not returned
|
||||
* by sg_err_category_sense() but are returned by some related functions. */
|
||||
#define SG_LIB_CAT_ILLEGAL_REQ_WITH_INFO 17 /* Illegal request (other than */
|
||||
/* invalid opcode) plus 'info' field: */
|
||||
/* [sk,asc,ascq: 0x5,*,*] */
|
||||
#define SG_LIB_CAT_MEDIUM_HARD_WITH_INFO 18 /* medium or hardware error */
|
||||
/* sense key plus 'info' field: */
|
||||
/* [sk,asc,ascq: 0x3/0x4,*,*] */
|
||||
#define SG_LIB_CAT_PROTECTION_WITH_INFO 41 /* aborted command sense key, */
|
||||
/* protection plus 'info' field: */
|
||||
/* [sk,asc,ascq: 0xb,0x10,*] */
|
||||
#define SG_LIB_CAT_TIMEOUT 33
|
||||
|
||||
/* Yield string associated with sense category. Returns 'buff' (or pointer
|
||||
* to "Bad sense category" if 'buff' is NULL). If sense_cat unknown then
|
||||
* yield "Sense category: <sense_cat>" string. */
|
||||
const char * sg_get_category_sense_str(int sense_cat, int buff_len,
|
||||
char * buff, int verbose);
|
||||
|
||||
|
||||
/* Iterates to next designation descriptor in the device identification
|
||||
* VPD page. The 'initial_desig_desc' should point to start of first
|
||||
* descriptor with 'page_len' being the number of valid bytes in that
|
||||
* and following descriptors. To start, 'off' should point to a negative
|
||||
* value, thereafter it should point to the value yielded by the previous
|
||||
* call. If 0 returned then 'initial_desig_desc + *off' should be a valid
|
||||
* descriptor; returns -1 if normal end condition and -2 for an abnormal
|
||||
* termination. Matches association, designator_type and/or code_set when
|
||||
* any of those values are greater than or equal to zero. */
|
||||
int sg_vpd_dev_id_iter(const unsigned char * initial_desig_desc, int page_len,
|
||||
int * off, int m_assoc, int m_desig_type,
|
||||
int m_code_set);
|
||||
|
||||
|
||||
/* <<< General purpose (i.e. not SCSI specific) utility functions >>> */
|
||||
|
||||
/* Always returns valid string even if errnum is wild (or library problem).
|
||||
* If errnum is negative, flip its sign. */
|
||||
char * safe_strerror(int errnum);
|
||||
|
||||
|
||||
/* Print (to stdout) 'str' of bytes in hex, 16 bytes per line optionally
|
||||
* followed at the right hand side of the line with an ASCII interpretation.
|
||||
* Each line is prefixed with an address, starting at 0 for str[0]..str[15].
|
||||
* All output numbers are in hex. 'no_ascii' allows for 3 output types:
|
||||
* > 0 each line has address then up to 16 ASCII-hex bytes
|
||||
* = 0 in addition, the bytes are listed in ASCII to the right
|
||||
* < 0 only the ASCII-hex bytes are listed (i.e. without address)
|
||||
*/
|
||||
void dStrHex(const char * str, int len, int no_ascii);
|
||||
|
||||
/* Print (to sg_warnings_strm (stderr)) 'str' of bytes in hex, 16 bytes per
|
||||
* line optionally followed at right by its ASCII interpretation. Same
|
||||
* logic as dStrHex() with different output stream (i.e. stderr). */
|
||||
void dStrHexErr(const char * str, int len, int no_ascii);
|
||||
|
||||
/* Read 'len' bytes from 'str' and output as ASCII-Hex bytes (space
|
||||
* separated) to 'b' not to exceed 'b_len' characters. Each line
|
||||
* starts with 'leadin' (NULL for no leadin) and there are 16 bytes
|
||||
* per line with an extra space between the 8th and 9th bytes. 'format'
|
||||
* is 0 for repeat in printable ASCII ('.' for non printable chars) to
|
||||
* right of each line; 1 don't (so just output ASCII hex). Returns
|
||||
* number of bytes written to 'b' excluding the trailing '\0'. */
|
||||
int dStrHexStr(const char * str, int len, const char * leadin, int format,
|
||||
int cb_len, char * cbp);
|
||||
|
||||
/* The following 3 functions are equivalent to dStrHex(), dStrHexErr() and
|
||||
* dStrHexStr() respectively. The difference is the type of the first of
|
||||
* argument: uint8_t instead of char. The name of the argument is changed
|
||||
* to b_str to stress it is a pointer to the start of a binary string. */
|
||||
void hex2stdout(const uint8_t * b_str, int len, int no_ascii);
|
||||
void hex2stderr(const uint8_t * b_str, int len, int no_ascii);
|
||||
int hex2str(const uint8_t * b_str, int len, const char * leadin, int format,
|
||||
int cb_len, char * cbp);
|
||||
|
||||
/* Returns true when executed on big endian machine; else returns false.
|
||||
* Useful for displaying ATA identify words (which need swapping on a
|
||||
* big endian machine). */
|
||||
bool sg_is_big_endian();
|
||||
|
||||
/* Returns true if byte sequence starting at bp with a length of b_len is
|
||||
* all zeros (for sg_all_zeros()) or all 0xff_s (for sg_all_ffs());
|
||||
* otherwise returns false. If bp is NULL ir b_len <= 0 returns false. */
|
||||
bool sg_all_zeros(const uint8_t * bp, int b_len);
|
||||
bool sg_all_ffs(const uint8_t * bp, int b_len);
|
||||
|
||||
/* Extract character sequence from ATA words as in the model string
|
||||
* in a IDENTIFY DEVICE response. Returns number of characters
|
||||
* written to 'ochars' before 0 character is found or 'num' words
|
||||
* are processed. */
|
||||
int sg_ata_get_chars(const uint16_t * word_arr, int start_word,
|
||||
int num_words, bool is_big_endian, char * ochars);
|
||||
|
||||
/* Print (to stdout) 16 bit 'words' in hex, 8 words per line optionally
|
||||
* followed at the right hand side of the line with an ASCII interpretation
|
||||
* (pairs of ASCII characters in big endian order (upper first)).
|
||||
* Each line is prefixed with an address, starting at 0.
|
||||
* All output numbers are in hex. 'no_ascii' allows for 3 output types:
|
||||
* > 0 each line has address then up to 8 ASCII-hex words
|
||||
* = 0 in addition, the words are listed in ASCII pairs to the right
|
||||
* = -1 only the ASCII-hex words are listed (i.e. without address)
|
||||
* = -2 only the ASCII-hex words, formatted for "hdparm --Istdin"
|
||||
* < -2 same as -1
|
||||
* If 'swapb' is true then bytes in each word swapped. Needs to be set
|
||||
* for ATA IDENTIFY DEVICE response on big-endian machines.
|
||||
*/
|
||||
void dWordHex(const uint16_t * words, int num, int no_ascii, bool swapb);
|
||||
|
||||
/* If the number in 'buf' can not be decoded or the multiplier is unknown
|
||||
* then -1 is returned. Accepts a hex prefix (0x or 0X) or a 'h' (or 'H')
|
||||
* suffix. Otherwise a decimal multiplier suffix may be given. Recognised
|
||||
* multipliers: c C *1; w W *2; b B *512; k K KiB *1,024;
|
||||
* KB *1,000; m M MiB *1,048,576; MB *1,000,000; g G GiB *1,073,741,824;
|
||||
* GB *1,000,000,000 and <n>x<m> which multiplies <n> by <m> . Ignore leading
|
||||
* spaces and tabs; accept comma, hyphen, space, tab and hash as terminator.
|
||||
*/
|
||||
int sg_get_num(const char * buf);
|
||||
|
||||
/* If the number in 'buf' can not be decoded then -1 is returned. Accepts a
|
||||
* hex prefix (0x or 0X) or a 'h' (or 'H') suffix; otherwise decimal is
|
||||
* assumed. Does not accept multipliers. Accept a comma (","), hyphen ("-"),
|
||||
* a whitespace or newline as terminator. Only decimal numbers can represent
|
||||
* negative numbers and '-1' must be treated separately. */
|
||||
int sg_get_num_nomult(const char * buf);
|
||||
|
||||
/* If the number in 'buf' can not be decoded or the multiplier is unknown
|
||||
* then -1LL is returned. Accepts a hex prefix (0x or 0X) or a 'h' (or 'H')
|
||||
* suffix. Otherwise a decimal multiplier suffix may be given. In addition
|
||||
* to supporting the multipliers of sg_get_num(), this function supports:
|
||||
* t T TiB *(2**40); TB *(10**12); p P PiB *(2**50); PB *(10**15) .
|
||||
* Ignore leading spaces and tabs; accept comma, hyphen, space, tab and hash
|
||||
* as terminator. */
|
||||
int64_t sg_get_llnum(const char * buf);
|
||||
|
||||
/* If the number in 'buf' can not be decoded then -1 is returned. Accepts a
|
||||
* hex prefix (0x or 0X) or a 'h' (or 'H') suffix; otherwise decimal is
|
||||
* assumed. Does not accept multipliers. Accept a comma (","), hyphen ("-"),
|
||||
* a whitespace or newline as terminator. Only decimal numbers can represent
|
||||
* negative numbers and '-1' must be treated separately. */
|
||||
int64_t sg_get_llnum_nomult(const char * buf);
|
||||
|
||||
/* Returns pointer to heap (or NULL) that is aligned to a align_to byte
|
||||
* boundary. Sends back *buff_to_free pointer in third argument that may be
|
||||
* different from the return value. If it is different then the *buff_to_free
|
||||
* pointer should be freed (rather than the returned value) when the heap is
|
||||
* no longer needed. If align_to is 0 then aligns to OS's page size. Sets all
|
||||
* returned heap to zeros. If num_bytes is 0 then set to page size. */
|
||||
uint8_t * sg_memalign(uint32_t num_bytes, uint32_t align_to,
|
||||
uint8_t ** buff_to_free, bool vb);
|
||||
|
||||
/* Returns OS page size in bytes. If uncertain returns 4096. */
|
||||
uint32_t sg_get_page_size(void);
|
||||
|
||||
/* If os_err_num is within bounds then the returned value is 'os_err_num +
|
||||
* SG_LIB_OS_BASE_ERR' otherwise -1 is returned. If os_err_num is 0 then 0
|
||||
* is returned. */
|
||||
int sg_convert_errno(int os_err_num);
|
||||
|
||||
|
||||
/* <<< Architectural support functions [is there a better place?] >>> */
|
||||
|
||||
/* Non Unix OSes distinguish between text and binary files.
|
||||
* Set text mode on fd. Does nothing in Unix. Returns negative number on
|
||||
* failure. */
|
||||
int sg_set_text_mode(int fd);
|
||||
|
||||
/* Set binary mode on fd. Does nothing in Unix. Returns negative number on
|
||||
* failure. */
|
||||
int sg_set_binary_mode(int fd);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* SG_LIB_H */
|
@ -1,121 +0,0 @@
|
||||
#ifndef SG_LIB_DATA_H
|
||||
#define SG_LIB_DATA_H
|
||||
|
||||
/*
|
||||
* Copyright (c) 2007-2018 Douglas Gilbert.
|
||||
* All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style
|
||||
* license that can be found in the BSD_LICENSE file.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This header file contains some structure declarations and array name
|
||||
* declarations which are defined in the sg_lib_data.c .
|
||||
* Typically this header does not need to be exposed to users of the
|
||||
* sg_lib interface declared in sg_libs.h .
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Operation codes with associated service actions that change or qualify
|
||||
* the command name */
|
||||
#define SG_EXTENDED_COPY 0x83 /* since spc4r34 became next entry */
|
||||
#define SG_3PARTY_COPY_OUT 0x83 /* new in spc4r34: Third party copy out */
|
||||
#define SG_RECEIVE_COPY 0x84 /* since spc4r34 became next entry */
|
||||
#define SG_3PARTY_COPY_IN 0x84 /* new in spc4r34: Third party copy in */
|
||||
#define SG_MAINTENANCE_IN 0xa3
|
||||
#define SG_MAINTENANCE_OUT 0xa4
|
||||
#define SG_PERSISTENT_RESERVE_IN 0x5e
|
||||
#define SG_PERSISTENT_RESERVE_OUT 0x5f
|
||||
#define SG_READ_ATTRIBUTE 0x8c
|
||||
#define SG_READ_BUFFER 0x3c /* now READ BUFFER(10) */
|
||||
#define SG_READ_BUFFER_16 0x9b
|
||||
#define SG_READ_POSITION 0x34 /* SSC command with service actions */
|
||||
#define SG_SANITIZE 0x48
|
||||
#define SG_SERVICE_ACTION_BIDI 0x9d
|
||||
#define SG_SERVICE_ACTION_IN_12 0xab
|
||||
#define SG_SERVICE_ACTION_IN_16 0x9e
|
||||
#define SG_SERVICE_ACTION_OUT_12 0xa9
|
||||
#define SG_SERVICE_ACTION_OUT_16 0x9f
|
||||
#define SG_VARIABLE_LENGTH_CMD 0x7f
|
||||
#define SG_WRITE_BUFFER 0x3b
|
||||
#define SG_ZONING_OUT 0x94
|
||||
#define SG_ZONING_IN 0x95
|
||||
|
||||
|
||||
|
||||
struct sg_lib_simple_value_name_t {
|
||||
int value;
|
||||
const char * name;
|
||||
};
|
||||
|
||||
struct sg_lib_value_name_t {
|
||||
int value;
|
||||
int peri_dev_type; /* 0 -> SPC and/or PDT_DISK, >0 -> PDT */
|
||||
const char * name;
|
||||
};
|
||||
|
||||
struct sg_lib_asc_ascq_t {
|
||||
unsigned char asc; /* additional sense code */
|
||||
unsigned char ascq; /* additional sense code qualifier */
|
||||
const char * text;
|
||||
};
|
||||
|
||||
struct sg_lib_asc_ascq_range_t {
|
||||
unsigned char asc; /* additional sense code (ASC) */
|
||||
unsigned char ascq_min; /* ASCQ minimum in range */
|
||||
unsigned char ascq_max; /* ASCQ maximum in range */
|
||||
const char * text;
|
||||
};
|
||||
|
||||
/* First use: SCSI status, sense_key, asc, ascq tuple */
|
||||
struct sg_lib_4tuple_u8 {
|
||||
uint8_t t1;
|
||||
uint8_t t2;
|
||||
uint8_t t3;
|
||||
uint8_t t4;
|
||||
};
|
||||
|
||||
|
||||
extern const char * sg_lib_version_str;
|
||||
|
||||
extern struct sg_lib_value_name_t sg_lib_normal_opcodes[];
|
||||
extern struct sg_lib_value_name_t sg_lib_read_buff_arr[];
|
||||
extern struct sg_lib_value_name_t sg_lib_write_buff_arr[];
|
||||
extern struct sg_lib_value_name_t sg_lib_maint_in_arr[];
|
||||
extern struct sg_lib_value_name_t sg_lib_maint_out_arr[];
|
||||
extern struct sg_lib_value_name_t sg_lib_pr_in_arr[];
|
||||
extern struct sg_lib_value_name_t sg_lib_pr_out_arr[];
|
||||
extern struct sg_lib_value_name_t sg_lib_sanitize_sa_arr[];
|
||||
extern struct sg_lib_value_name_t sg_lib_serv_in12_arr[];
|
||||
extern struct sg_lib_value_name_t sg_lib_serv_out12_arr[];
|
||||
extern struct sg_lib_value_name_t sg_lib_serv_in16_arr[];
|
||||
extern struct sg_lib_value_name_t sg_lib_serv_out16_arr[];
|
||||
extern struct sg_lib_value_name_t sg_lib_serv_bidi_arr[];
|
||||
extern struct sg_lib_value_name_t sg_lib_xcopy_sa_arr[];
|
||||
extern struct sg_lib_value_name_t sg_lib_rec_copy_sa_arr[];
|
||||
extern struct sg_lib_value_name_t sg_lib_variable_length_arr[];
|
||||
extern struct sg_lib_value_name_t sg_lib_zoning_out_arr[];
|
||||
extern struct sg_lib_value_name_t sg_lib_zoning_in_arr[];
|
||||
extern struct sg_lib_value_name_t sg_lib_read_attr_arr[];
|
||||
extern struct sg_lib_value_name_t sg_lib_read_pos_arr[];
|
||||
extern struct sg_lib_asc_ascq_range_t sg_lib_asc_ascq_range[];
|
||||
extern struct sg_lib_asc_ascq_t sg_lib_asc_ascq[];
|
||||
extern struct sg_lib_value_name_t sg_lib_scsi_feature_sets[];
|
||||
extern const char * sg_lib_sense_key_desc[];
|
||||
extern const char * sg_lib_pdt_strs[];
|
||||
extern const char * sg_lib_transport_proto_strs[];
|
||||
extern int sg_lib_pdt_decay_arr[];
|
||||
|
||||
extern struct sg_lib_value_name_t sg_lib_nvme_cmd_status_arr[];
|
||||
extern struct sg_lib_4tuple_u8 sg_lib_scsi_status_sense_arr[];
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
@ -1,56 +0,0 @@
|
||||
#ifndef SG_LINUX_INC_H
|
||||
#define SG_LINUX_INC_H
|
||||
|
||||
#ifdef SG_KERNEL_INCLUDES
|
||||
#define __user
|
||||
typedef unsigned char u8;
|
||||
#include "/usr/src/linux/include/scsi/sg.h"
|
||||
#include "/usr/src/linux/include/scsi/scsi.h"
|
||||
#else
|
||||
#ifdef SG_TRICK_GNU_INCLUDES
|
||||
#include <linux/../scsi/sg.h>
|
||||
#include <linux/../scsi/scsi.h>
|
||||
#else
|
||||
#include <scsi/sg.h>
|
||||
#include <scsi/scsi.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef BLKGETSIZE64
|
||||
#ifndef u64
|
||||
#include <stdint.h> /* C99 header for exact integer types */
|
||||
typedef uint64_t u64; /* problems with BLKGETSIZE64 ioctl in lk 2.4 */
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
Getting the correct include files for the sg interface can be an ordeal.
|
||||
In a perfect world, one would just write:
|
||||
#include <scsi/sg.h>
|
||||
#include <scsi/scsi.h>
|
||||
This would include the files found in the /usr/include/scsi directory.
|
||||
Those files are maintained with the GNU library which may or may not
|
||||
agree with the kernel and version of sg driver that is running. Any
|
||||
many cases this will not matter. However in some it might, for example
|
||||
glibc 2.1's include files match the sg driver found in the lk 2.2
|
||||
series. Hence if glibc 2.1 is used with lk 2.4 then the additional
|
||||
sg v3 interface will not be visible.
|
||||
If this is a problem then defining SG_KERNEL_INCLUDES will access the
|
||||
kernel supplied header files (assuming they are in the normal place).
|
||||
The GNU library maintainers and various kernel people don't like
|
||||
this approach (but it does work).
|
||||
The technique selected by defining SG_TRICK_GNU_INCLUDES worked (and
|
||||
was used) prior to glibc 2.2 . Prior to that version /usr/include/linux
|
||||
was a symbolic link to /usr/src/linux/include/linux .
|
||||
|
||||
There are other approaches if this include "mixup" causes pain. These
|
||||
would involve include files being copied or symbolic links being
|
||||
introduced.
|
||||
|
||||
Sorry about the inconvenience. Typically neither SG_KERNEL_INCLUDES
|
||||
nor SG_TRICK_GNU_INCLUDES is defined.
|
||||
|
||||
dpg 20010415, 20030522
|
||||
*/
|
||||
|
||||
#endif
|
@ -1,30 +0,0 @@
|
||||
#ifndef SG_PR2SERR_H
|
||||
#define SG_PR2SERR_H
|
||||
|
||||
/*
|
||||
* Copyright (c) 2004-2018 Douglas Gilbert.
|
||||
* All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style
|
||||
* license that can be found in the BSD_LICENSE file.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
int pr2serr(const char * fmt, ...)
|
||||
__attribute__ ((format (printf, 1, 2)));
|
||||
#else
|
||||
int pr2serr(const char * fmt, ...);
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
@ -1,215 +0,0 @@
|
||||
#ifndef SG_PT_H
|
||||
#define SG_PT_H
|
||||
|
||||
/*
|
||||
* Copyright (c) 2005-2018 Douglas Gilbert.
|
||||
* All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style
|
||||
* license that can be found in the BSD_LICENSE file.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* This declaration hides the fact that each implementation has its own
|
||||
* structure "derived" (using a C++ term) from this one. It compiles
|
||||
* because 'struct sg_pt_base' is only referenced (by pointer: 'objp')
|
||||
* in this interface. An instance of this structure represents the
|
||||
* context of one SCSI command. */
|
||||
struct sg_pt_base;
|
||||
|
||||
|
||||
/* The format of the version string is like this: "2.01 20090201".
|
||||
* The leading digit will be incremented if this interface changes
|
||||
* in a way that may impact backward compatibility. */
|
||||
const char * scsi_pt_version();
|
||||
|
||||
|
||||
/* Returns >= 0 if successful. If error in Unix returns negated errno. */
|
||||
int scsi_pt_open_device(const char * device_name, bool read_only, int verbose);
|
||||
|
||||
/* Similar to scsi_pt_open_device() but takes Unix style open flags OR-ed
|
||||
* together. Returns valid file descriptor( >= 0 ) if successful, otherwise
|
||||
* returns -1 or a negated errno.
|
||||
* In Win32 O_EXCL translated to equivalent. */
|
||||
int scsi_pt_open_flags(const char * device_name, int flags, int verbose);
|
||||
|
||||
/* Returns 0 if successful. If error in Unix returns negated errno. */
|
||||
int scsi_pt_close_device(int device_fd);
|
||||
|
||||
/* Assumes dev_fd is an "open" file handle associated with device_name. If
|
||||
* the implementation (possibly for one OS) cannot determine from dev_fd if
|
||||
* a SCSI or NVMe pass-through is referenced, then it might guess based on
|
||||
* device_name. Returns 1 if SCSI generic pass-though device, returns 2 if
|
||||
* secondary SCSI pass-through device (in Linux a bsg device); returns 3 is
|
||||
* char NVMe device (i.e. no NSID); returns 4 if block NVMe device (includes
|
||||
* NSID), or 0 if something else (e.g. ATA block device) or dev_fd < 0.
|
||||
* If error, returns negated errno (operating system) value. */
|
||||
int check_pt_file_handle(int dev_fd, const char * device_name, int verbose);
|
||||
|
||||
|
||||
/* Creates an object that can be used to issue one or more SCSI commands
|
||||
* (or task management functions). Returns NULL if problem.
|
||||
* Once this object has been created it should be destroyed with
|
||||
* destruct_scsi_pt_obj() when it is no longer needed. */
|
||||
struct sg_pt_base * construct_scsi_pt_obj(void);
|
||||
|
||||
/* An alternate way to create an object that can be used to issue one or
|
||||
* more SCSI commands (or task management functions). This variant
|
||||
* associate a device file descriptor (handle) with the object and a
|
||||
* verbose argument that causes error messages if errors occur. The
|
||||
* reason for this is to optionally allow the detection of NVMe devices
|
||||
* that will cause pt_device_is_nvme() to return true. Set dev_fd to
|
||||
* -1 if no open device file descriptor is available. Caller should
|
||||
* additionally call get_scsi_pt_os_err() after this call. */
|
||||
struct sg_pt_base *
|
||||
construct_scsi_pt_obj_with_fd(int dev_fd, int verbose);
|
||||
|
||||
/* Forget any previous dev_fd and install the one given. May attempt to
|
||||
* find file type (e.g. if pass-though) from OS so there could be an error.
|
||||
* Returns 0 for success or the same value as get_scsi_pt_os_err()
|
||||
* will return. dev_fd should be >= 0 for a valid file handle or -1 . */
|
||||
int set_pt_file_handle(struct sg_pt_base * objp, int dev_fd, int verbose);
|
||||
|
||||
/* Valid file handles (which is the return value) are >= 0 . Returns -1
|
||||
* if there is no valid file handle. */
|
||||
int get_pt_file_handle(const struct sg_pt_base * objp);
|
||||
|
||||
/* Clear state information held in *objp . This allows this object to be
|
||||
* used to issue more than one SCSI command. The dev_fd is remembered.
|
||||
* Use set_pt_file_handle() to change dev_fd. */
|
||||
void clear_scsi_pt_obj(struct sg_pt_base * objp);
|
||||
|
||||
/* Set the CDB (command descriptor block) */
|
||||
void set_scsi_pt_cdb(struct sg_pt_base * objp, const unsigned char * cdb,
|
||||
int cdb_len);
|
||||
/* Set the sense buffer and the maximum length that it can handle */
|
||||
void set_scsi_pt_sense(struct sg_pt_base * objp, unsigned char * sense,
|
||||
int max_sense_len);
|
||||
/* Set a pointer and length to be used for data transferred from device */
|
||||
void set_scsi_pt_data_in(struct sg_pt_base * objp, /* from device */
|
||||
unsigned char * dxferp, int dxfer_ilen);
|
||||
/* Set a pointer and length to be used for data transferred to device */
|
||||
void set_scsi_pt_data_out(struct sg_pt_base * objp, /* to device */
|
||||
const unsigned char * dxferp, int dxfer_olen);
|
||||
/* Set a pointer and length to be used for metadata transferred to
|
||||
* (out_true=true) or from (out_true-false) device */
|
||||
void set_pt_metadata_xfer(struct sg_pt_base * objp, unsigned char * mdxferp,
|
||||
uint32_t mdxfer_len, bool out_true);
|
||||
/* The following "set_"s implementations may be dummies */
|
||||
void set_scsi_pt_packet_id(struct sg_pt_base * objp, int pack_id);
|
||||
void set_scsi_pt_tag(struct sg_pt_base * objp, uint64_t tag);
|
||||
void set_scsi_pt_task_management(struct sg_pt_base * objp, int tmf_code);
|
||||
void set_scsi_pt_task_attr(struct sg_pt_base * objp, int attribute,
|
||||
int priority);
|
||||
|
||||
/* Following is a guard which is defined when set_scsi_pt_flags() is
|
||||
* present. Older versions of this library may not have this function. */
|
||||
#define SCSI_PT_FLAGS_FUNCTION 1
|
||||
/* If neither QUEUE_AT_HEAD nor QUEUE_AT_TAIL are given, or both
|
||||
* are given, use the pass-through default. */
|
||||
#define SCSI_PT_FLAGS_QUEUE_AT_TAIL 0x10
|
||||
#define SCSI_PT_FLAGS_QUEUE_AT_HEAD 0x20
|
||||
/* Set (potentially OS dependent) flags for pass-through mechanism.
|
||||
* Apart from contradictions, flags can be OR-ed together. */
|
||||
void set_scsi_pt_flags(struct sg_pt_base * objp, int flags);
|
||||
|
||||
#define SCSI_PT_DO_START_OK 0
|
||||
#define SCSI_PT_DO_BAD_PARAMS 1
|
||||
#define SCSI_PT_DO_TIMEOUT 2
|
||||
#define SCSI_PT_DO_NVME_STATUS 48 /* == SG_LIB_NVME_STATUS */
|
||||
/* If OS error prior to or during command submission then returns negated
|
||||
* error value (e.g. Unix '-errno'). This includes interrupted system calls
|
||||
* (e.g. by a signal) in which case -EINTR would be returned. Note that
|
||||
* system call errors also can be fetched with get_scsi_pt_os_err().
|
||||
* Return 0 if okay (i.e. at the very least: command sent). Positive
|
||||
* return values are errors (see SCSI_PT_DO_* defines). If a file descriptor
|
||||
* has already been provided by construct_scsi_pt_obj_with_fd() then the
|
||||
* given 'fd' can be -1 or the same value as given to the constructor. */
|
||||
int do_scsi_pt(struct sg_pt_base * objp, int fd, int timeout_secs,
|
||||
int verbose);
|
||||
|
||||
#define SCSI_PT_RESULT_GOOD 0
|
||||
#define SCSI_PT_RESULT_STATUS 1 /* other than GOOD and CHECK CONDITION */
|
||||
#define SCSI_PT_RESULT_SENSE 2
|
||||
#define SCSI_PT_RESULT_TRANSPORT_ERR 3
|
||||
#define SCSI_PT_RESULT_OS_ERR 4
|
||||
/* highest numbered applicable category returned */
|
||||
int get_scsi_pt_result_category(const struct sg_pt_base * objp);
|
||||
|
||||
/* If not available return 0 which implies there is no residual
|
||||
* value. If supported the number of bytes actually sent back by
|
||||
* the device is 'dxfer_ilen - get_scsi_pt_len()' bytes. */
|
||||
int get_scsi_pt_resid(const struct sg_pt_base * objp);
|
||||
|
||||
/* Returns SCSI status value (from device that received the command). If an
|
||||
* NVMe command was issued directly (i.e. through do_scsi_pt() then return
|
||||
* NVMe status (i.e. ((SCT << 8) | SC)) */
|
||||
int get_scsi_pt_status_response(const struct sg_pt_base * objp);
|
||||
|
||||
/* Returns SCSI status value or, if NVMe command given to do_scsi_pt(),
|
||||
* then returns NVMe result (i.e. DWord(0) from completion queue). If
|
||||
* 'objp' is NULL then returns 0xffffffff. */
|
||||
uint32_t get_pt_result(const struct sg_pt_base * objp);
|
||||
|
||||
/* Actual sense length returned. If sense data is present but
|
||||
actual sense length is not known, return 'max_sense_len' */
|
||||
int get_scsi_pt_sense_len(const struct sg_pt_base * objp);
|
||||
|
||||
/* If not available return 0 (for success). */
|
||||
int get_scsi_pt_os_err(const struct sg_pt_base * objp);
|
||||
char * get_scsi_pt_os_err_str(const struct sg_pt_base * objp, int max_b_len,
|
||||
char * b);
|
||||
|
||||
/* If not available return 0 (for success) */
|
||||
int get_scsi_pt_transport_err(const struct sg_pt_base * objp);
|
||||
void set_scsi_pt_transport_err(struct sg_pt_base * objp, int err);
|
||||
char * get_scsi_pt_transport_err_str(const struct sg_pt_base * objp,
|
||||
int max_b_len, char * b);
|
||||
|
||||
/* If not available return -1 */
|
||||
int get_scsi_pt_duration_ms(const struct sg_pt_base * objp);
|
||||
|
||||
/* Return true if device associated with 'objp' uses NVMe command set. To
|
||||
* be useful (in modifying the type of command sent (SCSI or NVMe) then
|
||||
* construct_scsi_pt_obj_with_fd() should be used followed by an invocation
|
||||
* of this function. */
|
||||
bool pt_device_is_nvme(const struct sg_pt_base * objp);
|
||||
|
||||
/* If a NVMe block device (which includes the NSID) handle is associated
|
||||
* with 'objp', then its NSID is returned (values range from 0x1 to
|
||||
* 0xffffffe). Otherwise 0 is returned. */
|
||||
uint32_t get_pt_nvme_nsid(const struct sg_pt_base * objp);
|
||||
|
||||
|
||||
/* Should be invoked once per objp after other processing is complete in
|
||||
* order to clean up resources. For ever successful construct_scsi_pt_obj()
|
||||
* call there should be one destruct_scsi_pt_obj(). If the
|
||||
* construct_scsi_pt_obj_with_fd() function was used to create this object
|
||||
* then the dev_fd provided to that constructor is not altered by this
|
||||
* destructor. So the user should still close dev_fd (perhaps with
|
||||
* scsi_pt_close_device() ). */
|
||||
void destruct_scsi_pt_obj(struct sg_pt_base * objp);
|
||||
|
||||
#ifdef SG_LIB_WIN32
|
||||
#define SG_LIB_WIN32_DIRECT 1
|
||||
|
||||
/* Request SPT direct interface when state_direct is 1, state_direct set
|
||||
* to 0 for the SPT indirect interface. Default setting selected by build
|
||||
* (i.e. library compile time) and is usually indirect. */
|
||||
void scsi_pt_win32_direct(int state_direct);
|
||||
|
||||
/* Returns current SPT interface state, 1 for direct, 0 for indirect */
|
||||
int scsi_pt_win32_spt_state(void);
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
@ -1,171 +0,0 @@
|
||||
#ifndef SG_PT_LINUX_H
|
||||
#define SG_PT_LINUX_H
|
||||
|
||||
/*
|
||||
* Copyright (c) 2017 Douglas Gilbert.
|
||||
* All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style
|
||||
* license that can be found in the BSD_LICENSE file.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "sg_pt_nvme.h"
|
||||
|
||||
/* This header is for internal use by the sg3_utils library (libsgutils)
|
||||
* and is Linux specific. Best not to include it directly in code that
|
||||
* is meant to be OS independent. */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_LINUX_BSG_H
|
||||
|
||||
#define BSG_PROTOCOL_SCSI 0
|
||||
|
||||
#define BSG_SUB_PROTOCOL_SCSI_CMD 0
|
||||
#define BSG_SUB_PROTOCOL_SCSI_TMF 1
|
||||
#define BSG_SUB_PROTOCOL_SCSI_TRANSPORT 2
|
||||
|
||||
/*
|
||||
* For flag constants below:
|
||||
* sg.h sg_io_hdr also has bits defined for it's flags member. These
|
||||
* two flag values (0x10 and 0x20) have the same meaning in sg.h . For
|
||||
* bsg the BSG_FLAG_Q_AT_HEAD flag is ignored since it is the default.
|
||||
*/
|
||||
#define BSG_FLAG_Q_AT_TAIL 0x10 /* default is Q_AT_HEAD */
|
||||
#define BSG_FLAG_Q_AT_HEAD 0x20
|
||||
|
||||
struct sg_io_v4 {
|
||||
__s32 guard; /* [i] 'Q' to differentiate from v3 */
|
||||
__u32 protocol; /* [i] 0 -> SCSI , .... */
|
||||
__u32 subprotocol; /* [i] 0 -> SCSI command, 1 -> SCSI task
|
||||
management function, .... */
|
||||
|
||||
__u32 request_len; /* [i] in bytes */
|
||||
__u64 request; /* [i], [*i] {SCSI: cdb} */
|
||||
__u64 request_tag; /* [i] {SCSI: task tag (only if flagged)} */
|
||||
__u32 request_attr; /* [i] {SCSI: task attribute} */
|
||||
__u32 request_priority; /* [i] {SCSI: task priority} */
|
||||
__u32 request_extra; /* [i] {spare, for padding} */
|
||||
__u32 max_response_len; /* [i] in bytes */
|
||||
__u64 response; /* [i], [*o] {SCSI: (auto)sense data} */
|
||||
|
||||
/* "dout_": data out (to device); "din_": data in (from device) */
|
||||
__u32 dout_iovec_count; /* [i] 0 -> "flat" dout transfer else
|
||||
dout_xfer points to array of iovec */
|
||||
__u32 dout_xfer_len; /* [i] bytes to be transferred to device */
|
||||
__u32 din_iovec_count; /* [i] 0 -> "flat" din transfer */
|
||||
__u32 din_xfer_len; /* [i] bytes to be transferred from device */
|
||||
__u64 dout_xferp; /* [i], [*i] */
|
||||
__u64 din_xferp; /* [i], [*o] */
|
||||
|
||||
__u32 timeout; /* [i] units: millisecond */
|
||||
__u32 flags; /* [i] bit mask */
|
||||
__u64 usr_ptr; /* [i->o] unused internally */
|
||||
__u32 spare_in; /* [i] */
|
||||
|
||||
__u32 driver_status; /* [o] 0 -> ok */
|
||||
__u32 transport_status; /* [o] 0 -> ok */
|
||||
__u32 device_status; /* [o] {SCSI: command completion status} */
|
||||
__u32 retry_delay; /* [o] {SCSI: status auxiliary information} */
|
||||
__u32 info; /* [o] additional information */
|
||||
__u32 duration; /* [o] time to complete, in milliseconds */
|
||||
__u32 response_len; /* [o] bytes of response actually written */
|
||||
__s32 din_resid; /* [o] din_xfer_len - actual_din_xfer_len */
|
||||
__s32 dout_resid; /* [o] dout_xfer_len - actual_dout_xfer_len */
|
||||
__u64 generated_tag; /* [o] {SCSI: transport generated task tag} */
|
||||
__u32 spare_out; /* [o] */
|
||||
|
||||
__u32 padding;
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
#include <linux/bsg.h>
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
struct sg_pt_linux_scsi {
|
||||
struct sg_io_v4 io_hdr; /* use v4 header as it is more general */
|
||||
/* Leave io_hdr in first place of this structure */
|
||||
bool is_sg;
|
||||
bool is_bsg;
|
||||
bool is_nvme; /* OS device type, if false ignore nvme_direct */
|
||||
bool nvme_direct; /* false: our SNTL; true: received NVMe command */
|
||||
bool mdxfer_out; /* direction of metadata xfer, true->data-out */
|
||||
bool scsi_dsense; /* SCSI descriptor sense active when true */
|
||||
int dev_fd; /* -1 if not given (yet) */
|
||||
int in_err;
|
||||
int os_err;
|
||||
uint32_t nvme_nsid; /* 1 to 0xfffffffe are possibly valid, 0
|
||||
* implies dev_fd is not a NVMe device
|
||||
* (is_nvme=false) or it is a NVMe char
|
||||
* device (e.g. /dev/nvme0 ) */
|
||||
uint32_t nvme_result; /* DW0 from completion queue */
|
||||
uint32_t nvme_status; /* SCT|SC: DW3 27:17 from completion queue,
|
||||
* note: the DNR+More bit are not there.
|
||||
* The whole 16 byte completion q entry is
|
||||
* sent back as sense data */
|
||||
uint32_t mdxfer_len;
|
||||
void * mdxferp;
|
||||
uint8_t * nvme_id_ctlp; /* cached response to controller IDENTIFY */
|
||||
uint8_t * free_nvme_id_ctlp;
|
||||
unsigned char tmf_request[4];
|
||||
};
|
||||
|
||||
struct sg_pt_base {
|
||||
struct sg_pt_linux_scsi impl;
|
||||
};
|
||||
|
||||
|
||||
#ifndef sg_nvme_admin_cmd
|
||||
#define sg_nvme_admin_cmd sg_nvme_passthru_cmd
|
||||
#endif
|
||||
|
||||
/* Linux NVMe related ioctls */
|
||||
#ifndef NVME_IOCTL_ID
|
||||
#define NVME_IOCTL_ID _IO('N', 0x40)
|
||||
#endif
|
||||
#ifndef NVME_IOCTL_ADMIN_CMD
|
||||
#define NVME_IOCTL_ADMIN_CMD _IOWR('N', 0x41, struct sg_nvme_admin_cmd)
|
||||
#endif
|
||||
#ifndef NVME_IOCTL_SUBMIT_IO
|
||||
#define NVME_IOCTL_SUBMIT_IO _IOW('N', 0x42, struct sg_nvme_user_io)
|
||||
#endif
|
||||
#ifndef NVME_IOCTL_IO_CMD
|
||||
#define NVME_IOCTL_IO_CMD _IOWR('N', 0x43, struct sg_nvme_passthru_cmd)
|
||||
#endif
|
||||
#ifndef NVME_IOCTL_RESET
|
||||
#define NVME_IOCTL_RESET _IO('N', 0x44)
|
||||
#endif
|
||||
#ifndef NVME_IOCTL_SUBSYS_RESET
|
||||
#define NVME_IOCTL_SUBSYS_RESET _IO('N', 0x45)
|
||||
#endif
|
||||
|
||||
extern bool sg_bsg_nvme_char_major_checked;
|
||||
extern int sg_bsg_major;
|
||||
extern volatile int sg_nvme_char_major;
|
||||
extern long sg_lin_page_size;
|
||||
|
||||
void sg_find_bsg_nvme_char_major(int verbose);
|
||||
int sg_do_nvme_pt(struct sg_pt_base * vp, int fd, int time_secs, int vb);
|
||||
|
||||
/* This trims given NVMe block device name in Linux (e.g. /dev/nvme0n1p5)
|
||||
* to the name of its associated char device (e.g. /dev/nvme0). If this
|
||||
* occurs true is returned and the char device name is placed in 'b' (as
|
||||
* long as b_len is sufficient). Otherwise false is returned. */
|
||||
bool sg_get_nvme_char_devname(const char * nvme_block_devname, uint32_t b_len,
|
||||
char * b);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* end of SG_PT_LINUX_H */
|
@ -1,172 +0,0 @@
|
||||
#ifndef SG_PT_NVME_H
|
||||
#define SG_PT_NVME_H
|
||||
|
||||
/*
|
||||
* Copyright (c) 2017-2018 Douglas Gilbert.
|
||||
* All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style
|
||||
* license that can be found in the BSD_LICENSE file.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* structures copied and slightly modified from <linux/nvme_ioctl.h> which
|
||||
* is Copyright (c) 2011-2014, Intel Corporation. */
|
||||
|
||||
|
||||
/* Note that the command input structure is in (packed) "cpu" format. That
|
||||
* means, for example, if the CPU is little endian (most are) then so is the
|
||||
* structure. However what comes out in the data-in buffer (e.g. for the
|
||||
* Admin Identify command response) is almost all little endian following ATA
|
||||
* (but no SCSI and IP which are big endian) and Intel's preference. There
|
||||
* are exceptions, for example the EUI-64 identifiers in the Admin Identify
|
||||
* response are big endian.
|
||||
*
|
||||
* Code online (e.g. nvme-cli at github.com) seems to like packed strcutures,
|
||||
* the author prefers byte offset plus a range of unaligned integer builders
|
||||
* such as those in sg_unaligned.h .
|
||||
*/
|
||||
|
||||
#ifdef __GNUC__
|
||||
#ifndef __clang__
|
||||
struct __attribute__((__packed__)) sg_nvme_user_io
|
||||
#else
|
||||
struct sg_nvme_user_io
|
||||
#endif
|
||||
#else
|
||||
struct sg_nvme_user_io
|
||||
#endif
|
||||
{
|
||||
uint8_t opcode;
|
||||
uint8_t flags;
|
||||
uint16_t control;
|
||||
uint16_t nblocks;
|
||||
uint16_t rsvd;
|
||||
uint64_t metadata;
|
||||
uint64_t addr;
|
||||
uint64_t slba;
|
||||
uint32_t dsmgmt;
|
||||
uint32_t reftag;
|
||||
uint16_t apptag;
|
||||
uint16_t appmask;
|
||||
}
|
||||
#ifdef SG_LIB_FREEBSD
|
||||
__packed;
|
||||
#else
|
||||
;
|
||||
#endif
|
||||
|
||||
/* Using byte offsets and unaligned be/le copies safer than packed
|
||||
* structures. These are for sg_nvme_user_io . */
|
||||
#define SG_NVME_IO_OPCODE 0
|
||||
#define SG_NVME_IO_FLAGS 1
|
||||
#define SG_NVME_IO_CONTROL 2
|
||||
#define SG_NVME_IO_NBLOCKS 4
|
||||
#define SG_NVME_IO_RSVD 6
|
||||
#define SG_NVME_IO_METADATA 8
|
||||
#define SG_NVME_IO_ADDR 16
|
||||
#define SG_NVME_IO_SLBA 24
|
||||
#define SG_NVME_IO_DSMGMT 32
|
||||
#define SG_NVME_IO_REFTAG 36
|
||||
#define SG_NVME_IO_APPTAG 40
|
||||
#define SG_NVME_IO_APPMASK 42
|
||||
|
||||
#ifdef __GNUC__
|
||||
#ifndef __clang__
|
||||
struct __attribute__((__packed__)) sg_nvme_passthru_cmd
|
||||
#else
|
||||
struct sg_nvme_passthru_cmd
|
||||
#endif
|
||||
#else
|
||||
struct sg_nvme_passthru_cmd
|
||||
#endif
|
||||
{
|
||||
uint8_t opcode;
|
||||
uint8_t flags;
|
||||
uint16_t rsvd1;
|
||||
uint32_t nsid;
|
||||
uint32_t cdw2;
|
||||
uint32_t cdw3;
|
||||
uint64_t metadata;
|
||||
uint64_t addr;
|
||||
uint32_t metadata_len;
|
||||
uint32_t data_len;
|
||||
uint32_t cdw10;
|
||||
uint32_t cdw11;
|
||||
uint32_t cdw12;
|
||||
uint32_t cdw13;
|
||||
uint32_t cdw14;
|
||||
uint32_t cdw15;
|
||||
#ifdef SG_LIB_LINUX
|
||||
uint32_t timeout_ms;
|
||||
uint32_t result; /* out: DWord(0) from completion queue */
|
||||
#endif
|
||||
}
|
||||
#ifdef SG_LIB_FREEBSD
|
||||
__packed;
|
||||
#else
|
||||
;
|
||||
#endif
|
||||
|
||||
|
||||
/* Using byte offsets and unaligned be/le copies safer than packed
|
||||
* structures. These are for sg_nvme_passthru_cmd . */
|
||||
#define SG_NVME_PT_OPCODE 0 /* length: 1 byte */
|
||||
#define SG_NVME_PT_FLAGS 1 /* length: 1 byte */
|
||||
#define SG_NVME_PT_RSVD1 2 /* length: 2 bytes */
|
||||
#define SG_NVME_PT_NSID 4 /* length: 4 bytes */
|
||||
#define SG_NVME_PT_CDW2 8 /* length: 4 bytes */
|
||||
#define SG_NVME_PT_CDW3 12 /* length: 4 bytes */
|
||||
#define SG_NVME_PT_METADATA 16 /* length: 8 bytes */
|
||||
#define SG_NVME_PT_ADDR 24 /* length: 8 bytes */
|
||||
#define SG_NVME_PT_METADATA_LEN 32 /* length: 4 bytes */
|
||||
#define SG_NVME_PT_DATA_LEN 36 /* length: 4 bytes */
|
||||
#define SG_NVME_PT_CDW10 40 /* length: 4 bytes */
|
||||
#define SG_NVME_PT_CDW11 44 /* length: 4 bytes */
|
||||
#define SG_NVME_PT_CDW12 48 /* length: 4 bytes */
|
||||
#define SG_NVME_PT_CDW13 52 /* length: 4 bytes */
|
||||
#define SG_NVME_PT_CDW14 56 /* length: 4 bytes */
|
||||
#define SG_NVME_PT_CDW15 60 /* length: 4 bytes */
|
||||
|
||||
#ifdef SG_LIB_LINUX
|
||||
/* General references state that "all NVMe commands are 64 bytes long". If
|
||||
* so then the following are add-ons by Linux, go to the OS and not the
|
||||
* the NVMe device. */
|
||||
#define SG_NVME_PT_TIMEOUT_MS 64 /* length: 4 bytes */
|
||||
#define SG_NVME_PT_RESULT 68 /* length: 4 bytes */
|
||||
#endif
|
||||
|
||||
/* Byte offset of Result and Status (plus phase bit) in CQ */
|
||||
#define SG_NVME_PT_CQ_RESULT 0 /* CDW0, length: 4 bytes */
|
||||
#define SG_NVME_PT_CQ_DW0 0 /* CDW0, length: 4 bytes */
|
||||
#define SG_NVME_PT_CQ_DW1 4 /* CDW1, length: 4 bytes */
|
||||
#define SG_NVME_PT_CQ_DW2 8 /* CDW2, length: 4 bytes */
|
||||
#define SG_NVME_PT_CQ_DW3 12 /* CDW3, length: 4 bytes */
|
||||
#define SG_NVME_PT_CQ_STATUS_P 14 /* CDW3 31:16, length: 2 bytes */
|
||||
|
||||
|
||||
/* Valid namespace IDs (nsid_s) range from 1 to 0xfffffffe, leaving: */
|
||||
#define SG_NVME_BROADCAST_NSID 0xffffffff /* all namespaces */
|
||||
#define SG_NVME_CTL_NSID 0x0 /* the "controller's" namespace */
|
||||
|
||||
/* Given the NVMe Identify Controller response and optionally the NVMe
|
||||
* Identify Namespace response (NULL otherwise), generate the SCSI VPD
|
||||
* page 0x83 (device identification) descriptor(s) in dop. Return the
|
||||
* number of bytes written which will not exceed max_do_len. Probably use
|
||||
* Peripheral Device Type (pdt) of 0 (disk) for don't know. Transport
|
||||
* protocol (tproto) should be -1 if not known, else SCSI value.
|
||||
* N.B. Does not write total VPD page length into dop[2:3] . */
|
||||
int sg_make_vpd_devid_for_nvme(const uint8_t * nvme_id_ctl_p,
|
||||
const uint8_t * nvme_id_ns_p, int pdt,
|
||||
int tproto, uint8_t * dop, int max_do_len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* SG_PT_NVME_H */
|
@ -1,473 +0,0 @@
|
||||
#ifndef SG_PT_WIN32_H
|
||||
#define SG_PT_WIN32_H
|
||||
/*
|
||||
* The information in this file was obtained from scsi-wnt.h by
|
||||
* Richard Stemmer, rs@epost.de . He in turn gives credit to
|
||||
* Jay A. Key (for scsipt.c).
|
||||
* The plscsi program (by Pat LaVarre <p.lavarre@ieee.org>) has
|
||||
* also been used as a reference.
|
||||
* Much of the information in this header can also be obtained
|
||||
* from msdn.microsoft.com .
|
||||
* Updated for cygwin version 1.7.17 changes 20121026
|
||||
*/
|
||||
|
||||
/* WIN32_LEAN_AND_MEAN may be required to prevent inclusion of <winioctl.h> */
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define SCSI_MAX_SENSE_LEN 64
|
||||
#define SCSI_MAX_CDB_LEN 16
|
||||
#define SCSI_MAX_INDIRECT_DATA 16384
|
||||
|
||||
typedef struct {
|
||||
USHORT Length;
|
||||
UCHAR ScsiStatus;
|
||||
UCHAR PathId;
|
||||
UCHAR TargetId;
|
||||
UCHAR Lun;
|
||||
UCHAR CdbLength;
|
||||
UCHAR SenseInfoLength;
|
||||
UCHAR DataIn;
|
||||
ULONG DataTransferLength;
|
||||
ULONG TimeOutValue;
|
||||
ULONG_PTR DataBufferOffset; /* was ULONG; problem in 64 bit */
|
||||
ULONG SenseInfoOffset;
|
||||
UCHAR Cdb[SCSI_MAX_CDB_LEN];
|
||||
} SCSI_PASS_THROUGH, *PSCSI_PASS_THROUGH;
|
||||
|
||||
|
||||
typedef struct {
|
||||
USHORT Length;
|
||||
UCHAR ScsiStatus;
|
||||
UCHAR PathId;
|
||||
UCHAR TargetId;
|
||||
UCHAR Lun;
|
||||
UCHAR CdbLength;
|
||||
UCHAR SenseInfoLength;
|
||||
UCHAR DataIn;
|
||||
ULONG DataTransferLength;
|
||||
ULONG TimeOutValue;
|
||||
PVOID DataBuffer;
|
||||
ULONG SenseInfoOffset;
|
||||
UCHAR Cdb[SCSI_MAX_CDB_LEN];
|
||||
} SCSI_PASS_THROUGH_DIRECT, *PSCSI_PASS_THROUGH_DIRECT;
|
||||
|
||||
|
||||
typedef struct {
|
||||
SCSI_PASS_THROUGH spt;
|
||||
/* plscsi shows a follow on 16 bytes allowing 32 byte cdb */
|
||||
ULONG Filler;
|
||||
UCHAR ucSenseBuf[SCSI_MAX_SENSE_LEN];
|
||||
UCHAR ucDataBuf[SCSI_MAX_INDIRECT_DATA];
|
||||
} SCSI_PASS_THROUGH_WITH_BUFFERS, *PSCSI_PASS_THROUGH_WITH_BUFFERS;
|
||||
|
||||
|
||||
typedef struct {
|
||||
SCSI_PASS_THROUGH_DIRECT spt;
|
||||
ULONG Filler;
|
||||
UCHAR ucSenseBuf[SCSI_MAX_SENSE_LEN];
|
||||
} SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, *PSCSI_PASS_THROUGH_DIRECT_WITH_BUFFER;
|
||||
|
||||
|
||||
|
||||
typedef struct {
|
||||
UCHAR NumberOfLogicalUnits;
|
||||
UCHAR InitiatorBusId;
|
||||
ULONG InquiryDataOffset;
|
||||
} SCSI_BUS_DATA, *PSCSI_BUS_DATA;
|
||||
|
||||
|
||||
typedef struct {
|
||||
UCHAR NumberOfBusses;
|
||||
SCSI_BUS_DATA BusData[1];
|
||||
} SCSI_ADAPTER_BUS_INFO, *PSCSI_ADAPTER_BUS_INFO;
|
||||
|
||||
|
||||
typedef struct {
|
||||
UCHAR PathId;
|
||||
UCHAR TargetId;
|
||||
UCHAR Lun;
|
||||
BOOLEAN DeviceClaimed;
|
||||
ULONG InquiryDataLength;
|
||||
ULONG NextInquiryDataOffset;
|
||||
UCHAR InquiryData[1];
|
||||
} SCSI_INQUIRY_DATA, *PSCSI_INQUIRY_DATA;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ULONG Length;
|
||||
UCHAR PortNumber;
|
||||
UCHAR PathId;
|
||||
UCHAR TargetId;
|
||||
UCHAR Lun;
|
||||
} SCSI_ADDRESS, *PSCSI_ADDRESS;
|
||||
|
||||
/*
|
||||
* Standard IOCTL define
|
||||
*/
|
||||
#ifndef CTL_CODE
|
||||
#define CTL_CODE(DevType, Function, Method, Access) \
|
||||
(((DevType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method))
|
||||
#endif
|
||||
|
||||
/*
|
||||
* file access values
|
||||
*/
|
||||
#ifndef FILE_ANY_ACCESS
|
||||
#define FILE_ANY_ACCESS 0
|
||||
#endif
|
||||
#ifndef FILE_READ_ACCESS
|
||||
#define FILE_READ_ACCESS 0x0001
|
||||
#endif
|
||||
#ifndef FILE_WRITE_ACCESS
|
||||
#define FILE_WRITE_ACCESS 0x0002
|
||||
#endif
|
||||
|
||||
// IOCTL_STORAGE_QUERY_PROPERTY
|
||||
|
||||
#define FILE_DEVICE_MASS_STORAGE 0x0000002d
|
||||
#define IOCTL_STORAGE_BASE FILE_DEVICE_MASS_STORAGE
|
||||
#define FILE_ANY_ACCESS 0
|
||||
|
||||
// #define METHOD_BUFFERED 0
|
||||
|
||||
#define IOCTL_STORAGE_QUERY_PROPERTY \
|
||||
CTL_CODE(IOCTL_STORAGE_BASE, 0x0500, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
|
||||
|
||||
#ifndef _DEVIOCTL_
|
||||
typedef enum _STORAGE_BUS_TYPE {
|
||||
BusTypeUnknown = 0x00,
|
||||
BusTypeScsi = 0x01,
|
||||
BusTypeAtapi = 0x02,
|
||||
BusTypeAta = 0x03,
|
||||
BusType1394 = 0x04,
|
||||
BusTypeSsa = 0x05,
|
||||
BusTypeFibre = 0x06,
|
||||
BusTypeUsb = 0x07,
|
||||
BusTypeRAID = 0x08,
|
||||
BusTypeiScsi = 0x09,
|
||||
BusTypeSas = 0x0A,
|
||||
BusTypeSata = 0x0B,
|
||||
BusTypeSd = 0x0C,
|
||||
BusTypeMmc = 0x0D,
|
||||
BusTypeVirtual = 0xE,
|
||||
BusTypeFileBackedVirtual = 0xF,
|
||||
BusTypeSpaces = 0x10,
|
||||
BusTypeNvme = 0x11,
|
||||
BusTypeSCM = 0x12,
|
||||
BusTypeUfs = 0x13,
|
||||
BusTypeMax = 0x14,
|
||||
BusTypeMaxReserved = 0x7F
|
||||
} STORAGE_BUS_TYPE, *PSTORAGE_BUS_TYPE;
|
||||
|
||||
typedef enum _STORAGE_PROTOCOL_TYPE {
|
||||
ProtocolTypeUnknown = 0,
|
||||
ProtocolTypeScsi,
|
||||
ProtocolTypeAta,
|
||||
ProtocolTypeNvme,
|
||||
ProtocolTypeSd
|
||||
} STORAGE_PROTOCOL_TYPE;
|
||||
|
||||
typedef enum _STORAGE_PROTOCOL_NVME_DATA_TYPE {
|
||||
NVMeDataTypeUnknown = 0,
|
||||
NVMeDataTypeIdentify,
|
||||
NVMeDataTypeLogPage,
|
||||
NVMeDataTypeFeature
|
||||
} STORAGE_PROTOCOL_NVME_DATA_TYPE;
|
||||
|
||||
typedef struct _STORAGE_PROTOCOL_SPECIFIC_DATA {
|
||||
STORAGE_PROTOCOL_TYPE ProtocolType;
|
||||
ULONG DataType;
|
||||
ULONG ProtocolDataRequestValue;
|
||||
ULONG ProtocolDataRequestSubValue;
|
||||
ULONG ProtocolDataOffset;
|
||||
ULONG ProtocolDataLength;
|
||||
ULONG FixedProtocolReturnData;
|
||||
ULONG Reserved[3];
|
||||
} STORAGE_PROTOCOL_SPECIFIC_DATA;
|
||||
|
||||
|
||||
typedef struct _STORAGE_DEVICE_DESCRIPTOR {
|
||||
ULONG Version;
|
||||
ULONG Size;
|
||||
UCHAR DeviceType;
|
||||
UCHAR DeviceTypeModifier;
|
||||
BOOLEAN RemovableMedia;
|
||||
BOOLEAN CommandQueueing;
|
||||
ULONG VendorIdOffset; /* 0 if not available */
|
||||
ULONG ProductIdOffset; /* 0 if not available */
|
||||
ULONG ProductRevisionOffset;/* 0 if not available */
|
||||
ULONG SerialNumberOffset; /* -1 if not available ?? */
|
||||
STORAGE_BUS_TYPE BusType;
|
||||
ULONG RawPropertiesLength;
|
||||
UCHAR RawDeviceProperties[1];
|
||||
} STORAGE_DEVICE_DESCRIPTOR, *PSTORAGE_DEVICE_DESCRIPTOR;
|
||||
|
||||
#define STORAGE_PROTOCOL_STRUCTURE_VERSION 0x1
|
||||
|
||||
#define IOCTL_STORAGE_PROTOCOL_COMMAND \
|
||||
CTL_CODE(IOCTL_STORAGE_BASE, 0x04F0, METHOD_BUFFERED, \
|
||||
FILE_READ_ACCESS | FILE_WRITE_ACCESS)
|
||||
|
||||
typedef struct _STORAGE_PROTOCOL_COMMAND {
|
||||
DWORD Version; /* STORAGE_PROTOCOL_STRUCTURE_VERSION */
|
||||
DWORD Length;
|
||||
STORAGE_PROTOCOL_TYPE ProtocolType;
|
||||
DWORD Flags;
|
||||
DWORD ReturnStatus;
|
||||
DWORD ErrorCode;
|
||||
DWORD CommandLength;
|
||||
DWORD ErrorInfoLength;
|
||||
DWORD DataToDeviceTransferLength;
|
||||
DWORD DataFromDeviceTransferLength;
|
||||
DWORD TimeOutValue;
|
||||
DWORD ErrorInfoOffset;
|
||||
DWORD DataToDeviceBufferOffset;
|
||||
DWORD DataFromDeviceBufferOffset;
|
||||
DWORD CommandSpecific;
|
||||
DWORD Reserved0;
|
||||
DWORD FixedProtocolReturnData;
|
||||
DWORD Reserved1[3];
|
||||
BYTE Command[1]; /* has CommandLength elements */
|
||||
} STORAGE_PROTOCOL_COMMAND, *PSTORAGE_PROTOCOL_COMMAND;
|
||||
|
||||
#endif /* _DEVIOCTL_ */
|
||||
|
||||
typedef struct _STORAGE_DEVICE_UNIQUE_IDENTIFIER {
|
||||
ULONG Version;
|
||||
ULONG Size;
|
||||
ULONG StorageDeviceIdOffset;
|
||||
ULONG StorageDeviceOffset;
|
||||
ULONG DriveLayoutSignatureOffset;
|
||||
} STORAGE_DEVICE_UNIQUE_IDENTIFIER, *PSTORAGE_DEVICE_UNIQUE_IDENTIFIER;
|
||||
|
||||
// Use CompareStorageDuids(PSTORAGE_DEVICE_UNIQUE_IDENTIFIER duid1, duid2)
|
||||
// to test for equality
|
||||
|
||||
#ifndef _DEVIOCTL_
|
||||
typedef enum _STORAGE_QUERY_TYPE {
|
||||
PropertyStandardQuery = 0,
|
||||
PropertyExistsQuery,
|
||||
PropertyMaskQuery,
|
||||
PropertyQueryMaxDefined
|
||||
} STORAGE_QUERY_TYPE, *PSTORAGE_QUERY_TYPE;
|
||||
|
||||
typedef enum _STORAGE_PROPERTY_ID {
|
||||
StorageDeviceProperty = 0,
|
||||
StorageAdapterProperty,
|
||||
StorageDeviceIdProperty,
|
||||
StorageDeviceUniqueIdProperty,
|
||||
StorageDeviceWriteCacheProperty,
|
||||
StorageMiniportProperty,
|
||||
StorageAccessAlignmentProperty,
|
||||
/* Identify controller goes to adapter; Identify namespace to device */
|
||||
StorageAdapterProtocolSpecificProperty = 49,
|
||||
StorageDeviceProtocolSpecificProperty = 50
|
||||
} STORAGE_PROPERTY_ID, *PSTORAGE_PROPERTY_ID;
|
||||
|
||||
typedef struct _STORAGE_PROPERTY_QUERY {
|
||||
STORAGE_PROPERTY_ID PropertyId;
|
||||
STORAGE_QUERY_TYPE QueryType;
|
||||
UCHAR AdditionalParameters[1];
|
||||
} STORAGE_PROPERTY_QUERY, *PSTORAGE_PROPERTY_QUERY;
|
||||
|
||||
typedef struct _STORAGE_PROTOCOL_DATA_DESCRIPTOR {
|
||||
DWORD Version;
|
||||
DWORD Size;
|
||||
STORAGE_PROTOCOL_SPECIFIC_DATA ProtocolSpecificData;
|
||||
} STORAGE_PROTOCOL_DATA_DESCRIPTOR, *PSTORAGE_PROTOCOL_DATA_DESCRIPTOR;
|
||||
|
||||
// Command completion status
|
||||
// The "Phase Tag" field and "Status Field" are separated in spec. We define
|
||||
// them in the same data structure to ease the memory access from software.
|
||||
//
|
||||
typedef union {
|
||||
struct {
|
||||
USHORT P : 1; // Phase Tag (P)
|
||||
|
||||
USHORT SC : 8; // Status Code (SC)
|
||||
USHORT SCT : 3; // Status Code Type (SCT)
|
||||
USHORT Reserved : 2;
|
||||
USHORT M : 1; // More (M)
|
||||
USHORT DNR : 1; // Do Not Retry (DNR)
|
||||
} DUMMYSTRUCTNAME;
|
||||
USHORT AsUshort;
|
||||
} NVME_COMMAND_STATUS, *PNVME_COMMAND_STATUS;
|
||||
|
||||
// Information of log: NVME_LOG_PAGE_ERROR_INFO. Size: 64 bytes
|
||||
//
|
||||
typedef struct {
|
||||
ULONGLONG ErrorCount;
|
||||
USHORT SQID; // Submission Queue ID
|
||||
USHORT CMDID; // Command ID
|
||||
NVME_COMMAND_STATUS Status; // Status Field: This field indicates the
|
||||
// Status Field for the command that
|
||||
// completed. The Status Field is located in
|
||||
// bits 15:01, bit 00 corresponds to the Phase
|
||||
// Tag posted for the command.
|
||||
struct {
|
||||
USHORT Byte : 8; // Byte in command that contained error
|
||||
USHORT Bit : 3; // Bit in command that contained error
|
||||
USHORT Reserved : 5;
|
||||
} ParameterErrorLocation;
|
||||
|
||||
ULONGLONG Lba; // LBA: This field indicates the first LBA
|
||||
// that experienced the error condition, if
|
||||
// applicable.
|
||||
ULONG NameSpace; // Namespace: This field indicates the nsid
|
||||
// that the error is associated with, if
|
||||
// applicable.
|
||||
UCHAR VendorInfoAvailable; // Vendor Specific Information Available
|
||||
UCHAR Reserved0[3];
|
||||
ULONGLONG CommandSpecificInfo; // This field contains command specific
|
||||
// information. If used, the command
|
||||
// definition specifies the information
|
||||
// returned.
|
||||
UCHAR Reserved1[24];
|
||||
} NVME_ERROR_INFO_LOG, *PNVME_ERROR_INFO_LOG;
|
||||
|
||||
typedef struct {
|
||||
|
||||
ULONG DW0;
|
||||
ULONG Reserved;
|
||||
|
||||
union {
|
||||
struct {
|
||||
USHORT SQHD; // SQ Head Pointer (SQHD)
|
||||
USHORT SQID; // SQ Identifier (SQID)
|
||||
} DUMMYSTRUCTNAME;
|
||||
|
||||
ULONG AsUlong;
|
||||
} DW2;
|
||||
|
||||
union {
|
||||
struct {
|
||||
USHORT CID; // Command Identifier (CID)
|
||||
NVME_COMMAND_STATUS Status;
|
||||
} DUMMYSTRUCTNAME;
|
||||
|
||||
ULONG AsUlong;
|
||||
} DW3;
|
||||
|
||||
} NVME_COMPLETION_ENTRY, *PNVME_COMPLETION_ENTRY;
|
||||
|
||||
|
||||
// Bit-mask values for STORAGE_PROTOCOL_COMMAND - "Flags" field.
|
||||
//
|
||||
// Flag indicates the request targeting to adapter instead of device.
|
||||
#define STORAGE_PROTOCOL_COMMAND_FLAG_ADAPTER_REQUEST 0x80000000
|
||||
|
||||
//
|
||||
// Status values for STORAGE_PROTOCOL_COMMAND - "ReturnStatus" field.
|
||||
//
|
||||
#define STORAGE_PROTOCOL_STATUS_PENDING 0x0
|
||||
#define STORAGE_PROTOCOL_STATUS_SUCCESS 0x1
|
||||
#define STORAGE_PROTOCOL_STATUS_ERROR 0x2
|
||||
#define STORAGE_PROTOCOL_STATUS_INVALID_REQUEST 0x3
|
||||
#define STORAGE_PROTOCOL_STATUS_NO_DEVICE 0x4
|
||||
#define STORAGE_PROTOCOL_STATUS_BUSY 0x5
|
||||
#define STORAGE_PROTOCOL_STATUS_DATA_OVERRUN 0x6
|
||||
#define STORAGE_PROTOCOL_STATUS_INSUFFICIENT_RESOURCES 0x7
|
||||
|
||||
#define STORAGE_PROTOCOL_STATUS_NOT_SUPPORTED 0xFF
|
||||
|
||||
// Command Length for Storage Protocols.
|
||||
//
|
||||
// NVMe commands are always 64 bytes.
|
||||
#define STORAGE_PROTOCOL_COMMAND_LENGTH_NVME 0x40
|
||||
|
||||
// Command Specific Information for Storage Protocols - CommandSpecific field
|
||||
//
|
||||
#define STORAGE_PROTOCOL_SPECIFIC_NVME_ADMIN_COMMAND 0x01
|
||||
#define STORAGE_PROTOCOL_SPECIFIC_NVME_NVM_COMMAND 0x02
|
||||
|
||||
#endif /* _DEVIOCTL_ */
|
||||
|
||||
|
||||
// NVME_PASS_THROUGH
|
||||
|
||||
#ifndef STB_IO_CONTROL
|
||||
typedef struct _SRB_IO_CONTROL {
|
||||
ULONG HeaderLength;
|
||||
UCHAR Signature[8];
|
||||
ULONG Timeout;
|
||||
ULONG ControlCode;
|
||||
ULONG ReturnCode;
|
||||
ULONG Length;
|
||||
} SRB_IO_CONTROL, *PSRB_IO_CONTROL;
|
||||
#endif
|
||||
|
||||
#ifndef NVME_PASS_THROUGH_SRB_IO_CODE
|
||||
|
||||
#define NVME_SIG_STR "NvmeMini"
|
||||
#define NVME_STORPORT_DRIVER 0xe000
|
||||
|
||||
#define NVME_PASS_THROUGH_SRB_IO_CODE \
|
||||
CTL_CODE(NVME_STORPORT_DRIVER, 0x0800, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
|
||||
#pragma pack(1)
|
||||
|
||||
/* Following is pre-Win10; used with DeviceIoControl(IOCTL_SCSI_MINIPORT),
|
||||
* in Win10 need DeviceIoControl(IOCTL_STORAGE_PROTOCOL_COMMAND) for pure
|
||||
* pass-through. Win10 also has "Protocol specific queries" for things like
|
||||
* Identify and Get feature. */
|
||||
typedef struct _NVME_PASS_THROUGH_IOCTL
|
||||
{
|
||||
SRB_IO_CONTROL SrbIoCtrl;
|
||||
ULONG VendorSpecific[6];
|
||||
ULONG NVMeCmd[16]; /* Command DW[0...15] */
|
||||
ULONG CplEntry[4]; /* Completion DW[0...3] */
|
||||
ULONG Direction; /* 0=None, 1=Out, 2=In, 3=I/O */
|
||||
ULONG QueueId; /* 0=AdminQ */
|
||||
ULONG DataBufferLen; /* sizeof(DataBuffer) if Data In */
|
||||
ULONG MetaDataLen;
|
||||
ULONG ReturnBufferLen; /* offsetof(DataBuffer), plus
|
||||
* sizeof(DataBuffer) if Data Out */
|
||||
UCHAR DataBuffer[1];
|
||||
} NVME_PASS_THROUGH_IOCTL;
|
||||
#pragma pack()
|
||||
|
||||
#endif // NVME_PASS_THROUGH_SRB_IO_CODE
|
||||
|
||||
|
||||
/*
|
||||
* method codes
|
||||
*/
|
||||
#define METHOD_BUFFERED 0
|
||||
#define METHOD_IN_DIRECT 1
|
||||
#define METHOD_OUT_DIRECT 2
|
||||
#define METHOD_NEITHER 3
|
||||
|
||||
|
||||
#define IOCTL_SCSI_BASE 0x00000004
|
||||
|
||||
/*
|
||||
* constants for DataIn member of SCSI_PASS_THROUGH* structures
|
||||
*/
|
||||
#define SCSI_IOCTL_DATA_OUT 0
|
||||
#define SCSI_IOCTL_DATA_IN 1
|
||||
#define SCSI_IOCTL_DATA_UNSPECIFIED 2
|
||||
|
||||
#define IOCTL_SCSI_PASS_THROUGH CTL_CODE(IOCTL_SCSI_BASE, 0x0401, \
|
||||
METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
|
||||
#define IOCTL_SCSI_MINIPORT CTL_CODE(IOCTL_SCSI_BASE, 0x0402, \
|
||||
METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
|
||||
#define IOCTL_SCSI_GET_INQUIRY_DATA CTL_CODE(IOCTL_SCSI_BASE, 0x0403, \
|
||||
METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define IOCTL_SCSI_GET_CAPABILITIES CTL_CODE(IOCTL_SCSI_BASE, 0x0404, \
|
||||
METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define IOCTL_SCSI_PASS_THROUGH_DIRECT CTL_CODE(IOCTL_SCSI_BASE, 0x0405, \
|
||||
METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
|
||||
#define IOCTL_SCSI_GET_ADDRESS CTL_CODE(IOCTL_SCSI_BASE, 0x0406, \
|
||||
METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
@ -1,325 +0,0 @@
|
||||
#ifndef SG_UNALIGNED_H
|
||||
#define SG_UNALIGNED_H
|
||||
|
||||
/*
|
||||
* Copyright (c) 2014-2017 Douglas Gilbert.
|
||||
* All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style
|
||||
* license that can be found in the BSD_LICENSE file.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Borrowed from the Linux kernel, via mhvtl */
|
||||
|
||||
/* In the first section below, functions that copy unsigned integers in a
|
||||
* computer's native format, to and from an unaligned big endian sequence of
|
||||
* bytes. Big endian byte format "on the wire" is the default used by SCSI
|
||||
* standards (www.t10.org). Big endian is also the network byte order. */
|
||||
|
||||
static inline uint16_t __get_unaligned_be16(const uint8_t *p)
|
||||
{
|
||||
return p[0] << 8 | p[1];
|
||||
}
|
||||
|
||||
static inline uint32_t __get_unaligned_be32(const uint8_t *p)
|
||||
{
|
||||
return p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3];
|
||||
}
|
||||
|
||||
/* Assume 48 bit value placed in uint64_t */
|
||||
static inline uint64_t __get_unaligned_be48(const uint8_t *p)
|
||||
{
|
||||
return (uint64_t)__get_unaligned_be16(p) << 32 |
|
||||
__get_unaligned_be32(p + 2);
|
||||
}
|
||||
|
||||
static inline uint64_t __get_unaligned_be64(const uint8_t *p)
|
||||
{
|
||||
return (uint64_t)__get_unaligned_be32(p) << 32 |
|
||||
__get_unaligned_be32(p + 4);
|
||||
}
|
||||
|
||||
static inline void __put_unaligned_be16(uint16_t val, uint8_t *p)
|
||||
{
|
||||
*p++ = val >> 8;
|
||||
*p++ = val;
|
||||
}
|
||||
|
||||
static inline void __put_unaligned_be32(uint32_t val, uint8_t *p)
|
||||
{
|
||||
__put_unaligned_be16(val >> 16, p);
|
||||
__put_unaligned_be16(val, p + 2);
|
||||
}
|
||||
|
||||
/* Assume 48 bit value placed in uint64_t */
|
||||
static inline void __put_unaligned_be48(uint64_t val, uint8_t *p)
|
||||
{
|
||||
__put_unaligned_be16(val >> 32, p);
|
||||
__put_unaligned_be32(val, p + 2);
|
||||
}
|
||||
|
||||
static inline void __put_unaligned_be64(uint64_t val, uint8_t *p)
|
||||
{
|
||||
__put_unaligned_be32(val >> 32, p);
|
||||
__put_unaligned_be32(val, p + 4);
|
||||
}
|
||||
|
||||
static inline uint16_t sg_get_unaligned_be16(const void *p)
|
||||
{
|
||||
return __get_unaligned_be16((const uint8_t *)p);
|
||||
}
|
||||
|
||||
static inline uint32_t sg_get_unaligned_be24(const void *p)
|
||||
{
|
||||
return ((const uint8_t *)p)[0] << 16 | ((const uint8_t *)p)[1] << 8 |
|
||||
((const uint8_t *)p)[2];
|
||||
}
|
||||
|
||||
static inline uint32_t sg_get_unaligned_be32(const void *p)
|
||||
{
|
||||
return __get_unaligned_be32((const uint8_t *)p);
|
||||
}
|
||||
|
||||
/* Assume 48 bit value placed in uint64_t */
|
||||
static inline uint64_t sg_get_unaligned_be48(const void *p)
|
||||
{
|
||||
return __get_unaligned_be48((const uint8_t *)p);
|
||||
}
|
||||
|
||||
static inline uint64_t sg_get_unaligned_be64(const void *p)
|
||||
{
|
||||
return __get_unaligned_be64((const uint8_t *)p);
|
||||
}
|
||||
|
||||
/* Returns 0 if 'num_bytes' is less than or equal to 0 or greater than
|
||||
* 8 (i.e. sizeof(uint64_t)). Else returns result in uint64_t which is
|
||||
* an 8 byte unsigned integer. */
|
||||
static inline uint64_t sg_get_unaligned_be(int num_bytes, const void *p)
|
||||
{
|
||||
if ((num_bytes <= 0) || (num_bytes > (int)sizeof(uint64_t)))
|
||||
return 0;
|
||||
else {
|
||||
const uint8_t * xp = (const uint8_t *)p;
|
||||
uint64_t res = *xp;
|
||||
|
||||
for (++xp; num_bytes > 1; ++xp, --num_bytes)
|
||||
res = (res << 8) | *xp;
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void sg_put_unaligned_be16(uint16_t val, void *p)
|
||||
{
|
||||
__put_unaligned_be16(val, (uint8_t *)p);
|
||||
}
|
||||
|
||||
static inline void sg_put_unaligned_be24(uint32_t val, void *p)
|
||||
{
|
||||
((uint8_t *)p)[0] = (val >> 16) & 0xff;
|
||||
((uint8_t *)p)[1] = (val >> 8) & 0xff;
|
||||
((uint8_t *)p)[2] = val & 0xff;
|
||||
}
|
||||
|
||||
static inline void sg_put_unaligned_be32(uint32_t val, void *p)
|
||||
{
|
||||
__put_unaligned_be32(val, (uint8_t *)p);
|
||||
}
|
||||
|
||||
/* Assume 48 bit value placed in uint64_t */
|
||||
static inline void sg_put_unaligned_be48(uint64_t val, void *p)
|
||||
{
|
||||
__put_unaligned_be48(val, (uint8_t *)p);
|
||||
}
|
||||
|
||||
static inline void sg_put_unaligned_be64(uint64_t val, void *p)
|
||||
{
|
||||
__put_unaligned_be64(val, (uint8_t *)p);
|
||||
}
|
||||
|
||||
/* Since cdb and parameter blocks are often memset to zero before these
|
||||
* unaligned function partially fill them, then check for a val of zero
|
||||
* and ignore if it is with these variants. */
|
||||
static inline void sg_nz_put_unaligned_be16(uint16_t val, void *p)
|
||||
{
|
||||
if (val)
|
||||
__put_unaligned_be16(val, (uint8_t *)p);
|
||||
}
|
||||
|
||||
static inline void sg_nz_put_unaligned_be24(uint32_t val, void *p)
|
||||
{
|
||||
if (val) {
|
||||
((uint8_t *)p)[0] = (val >> 16) & 0xff;
|
||||
((uint8_t *)p)[1] = (val >> 8) & 0xff;
|
||||
((uint8_t *)p)[2] = val & 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void sg_nz_put_unaligned_be32(uint32_t val, void *p)
|
||||
{
|
||||
if (val)
|
||||
__put_unaligned_be32(val, (uint8_t *)p);
|
||||
}
|
||||
|
||||
static inline void sg_nz_put_unaligned_be64(uint64_t val, void *p)
|
||||
{
|
||||
if (val)
|
||||
__put_unaligned_be64(val, (uint8_t *)p);
|
||||
}
|
||||
|
||||
|
||||
/* Below are the little endian equivalents of the big endian functions
|
||||
* above. Little endian is used by ATA, PCI and NVMe.
|
||||
*/
|
||||
|
||||
static inline uint16_t __get_unaligned_le16(const uint8_t *p)
|
||||
{
|
||||
return p[1] << 8 | p[0];
|
||||
}
|
||||
|
||||
static inline uint32_t __get_unaligned_le32(const uint8_t *p)
|
||||
{
|
||||
return p[3] << 24 | p[2] << 16 | p[1] << 8 | p[0];
|
||||
}
|
||||
|
||||
static inline uint64_t __get_unaligned_le64(const uint8_t *p)
|
||||
{
|
||||
return (uint64_t)__get_unaligned_le32(p + 4) << 32 |
|
||||
__get_unaligned_le32(p);
|
||||
}
|
||||
|
||||
static inline void __put_unaligned_le16(uint16_t val, uint8_t *p)
|
||||
{
|
||||
*p++ = val;
|
||||
*p++ = val >> 8;
|
||||
}
|
||||
|
||||
static inline void __put_unaligned_le32(uint32_t val, uint8_t *p)
|
||||
{
|
||||
__put_unaligned_le16(val >> 16, p + 2);
|
||||
__put_unaligned_le16(val, p);
|
||||
}
|
||||
|
||||
static inline void __put_unaligned_le64(uint64_t val, uint8_t *p)
|
||||
{
|
||||
__put_unaligned_le32(val >> 32, p + 4);
|
||||
__put_unaligned_le32(val, p);
|
||||
}
|
||||
|
||||
static inline uint16_t sg_get_unaligned_le16(const void *p)
|
||||
{
|
||||
return __get_unaligned_le16((const uint8_t *)p);
|
||||
}
|
||||
|
||||
static inline uint32_t sg_get_unaligned_le24(const void *p)
|
||||
{
|
||||
return (uint32_t)__get_unaligned_le16((const uint8_t *)p) |
|
||||
((const uint8_t *)p)[2] << 16;
|
||||
}
|
||||
|
||||
static inline uint32_t sg_get_unaligned_le32(const void *p)
|
||||
{
|
||||
return __get_unaligned_le32((const uint8_t *)p);
|
||||
}
|
||||
|
||||
/* Assume 48 bit value placed in uint64_t */
|
||||
static inline uint64_t sg_get_unaligned_le48(const void *p)
|
||||
{
|
||||
return (uint64_t)__get_unaligned_le16((const uint8_t *)p + 4) << 32 |
|
||||
__get_unaligned_le32((const uint8_t *)p);
|
||||
}
|
||||
|
||||
static inline uint64_t sg_get_unaligned_le64(const void *p)
|
||||
{
|
||||
return __get_unaligned_le64((const uint8_t *)p);
|
||||
}
|
||||
|
||||
/* Returns 0 if 'num_bytes' is less than or equal to 0 or greater than
|
||||
* 8 (i.e. sizeof(uint64_t)). Else returns result in uint64_t which is
|
||||
* an 8 byte unsigned integer. */
|
||||
static inline uint64_t sg_get_unaligned_le(int num_bytes, const void *p)
|
||||
{
|
||||
if ((num_bytes <= 0) || (num_bytes > (int)sizeof(uint64_t)))
|
||||
return 0;
|
||||
else {
|
||||
const uint8_t * xp = (const uint8_t *)p + (num_bytes - 1);
|
||||
uint64_t res = *xp;
|
||||
|
||||
for (--xp; num_bytes > 1; --xp, --num_bytes)
|
||||
res = (res << 8) | *xp;
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void sg_put_unaligned_le16(uint16_t val, void *p)
|
||||
{
|
||||
__put_unaligned_le16(val, (uint8_t *)p);
|
||||
}
|
||||
|
||||
static inline void sg_put_unaligned_le24(uint32_t val, void *p)
|
||||
{
|
||||
((uint8_t *)p)[2] = (val >> 16) & 0xff;
|
||||
((uint8_t *)p)[1] = (val >> 8) & 0xff;
|
||||
((uint8_t *)p)[0] = val & 0xff;
|
||||
}
|
||||
|
||||
static inline void sg_put_unaligned_le32(uint32_t val, void *p)
|
||||
{
|
||||
__put_unaligned_le32(val, (uint8_t *)p);
|
||||
}
|
||||
|
||||
/* Assume 48 bit value placed in uint64_t */
|
||||
static inline void sg_put_unaligned_le48(uint64_t val, void *p)
|
||||
{
|
||||
((uint8_t *)p)[5] = (val >> 40) & 0xff;
|
||||
((uint8_t *)p)[4] = (val >> 32) & 0xff;
|
||||
((uint8_t *)p)[3] = (val >> 24) & 0xff;
|
||||
((uint8_t *)p)[2] = (val >> 16) & 0xff;
|
||||
((uint8_t *)p)[1] = (val >> 8) & 0xff;
|
||||
((uint8_t *)p)[0] = val & 0xff;
|
||||
}
|
||||
|
||||
static inline void sg_put_unaligned_le64(uint64_t val, void *p)
|
||||
{
|
||||
__put_unaligned_le64(val, (uint8_t *)p);
|
||||
}
|
||||
|
||||
/* Since cdb and parameter blocks are often memset to zero before these
|
||||
* unaligned function partially fill them, then check for a val of zero
|
||||
* and ignore if it is with these variants. */
|
||||
static inline void sg_nz_put_unaligned_le16(uint16_t val, void *p)
|
||||
{
|
||||
if (val)
|
||||
__put_unaligned_le16(val, (uint8_t *)p);
|
||||
}
|
||||
|
||||
static inline void sg_nz_put_unaligned_le24(uint32_t val, void *p)
|
||||
{
|
||||
if (val) {
|
||||
((uint8_t *)p)[2] = (val >> 16) & 0xff;
|
||||
((uint8_t *)p)[1] = (val >> 8) & 0xff;
|
||||
((uint8_t *)p)[0] = val & 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void sg_nz_put_unaligned_le32(uint32_t val, void *p)
|
||||
{
|
||||
if (val)
|
||||
__put_unaligned_le32(val, (uint8_t *)p);
|
||||
}
|
||||
|
||||
static inline void sg_nz_put_unaligned_le64(uint64_t val, void *p)
|
||||
{
|
||||
if (val)
|
||||
__put_unaligned_le64(val, (uint8_t *)p);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* SG_UNALIGNED_H */
|
@ -1,663 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 1999-2018 Douglas Gilbert.
|
||||
* All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style
|
||||
* license that can be found in the BSD_LICENSE file.
|
||||
*/
|
||||
|
||||
/*
|
||||
* CONTENTS
|
||||
* Some SCSI commands are executed in many contexts and hence began
|
||||
* to appear in several sg3_utils utilities. This files centralizes
|
||||
* some of the low level command execution code. In most cases the
|
||||
* interpretation of the command response is left to the each
|
||||
* utility.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "sg_lib.h"
|
||||
#include "sg_cmds_basic.h"
|
||||
#include "sg_pt.h"
|
||||
#include "sg_unaligned.h"
|
||||
|
||||
/* Needs to be after config.h */
|
||||
#ifdef SG_LIB_LINUX
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
|
||||
static const char * const version_str = "1.83 20180204";
|
||||
|
||||
|
||||
#define SENSE_BUFF_LEN 64 /* Arbitrary, could be larger */
|
||||
#define EBUFF_SZ 256
|
||||
|
||||
#define DEF_PT_TIMEOUT 60 /* 60 seconds */
|
||||
#define START_PT_TIMEOUT 120 /* 120 seconds == 2 minutes */
|
||||
#define LONG_PT_TIMEOUT 7200 /* 7,200 seconds == 120 minutes */
|
||||
|
||||
#define INQUIRY_CMD 0x12
|
||||
#define INQUIRY_CMDLEN 6
|
||||
#define REQUEST_SENSE_CMD 0x3
|
||||
#define REQUEST_SENSE_CMDLEN 6
|
||||
#define REPORT_LUNS_CMD 0xa0
|
||||
#define REPORT_LUNS_CMDLEN 12
|
||||
#define TUR_CMD 0x0
|
||||
#define TUR_CMDLEN 6
|
||||
|
||||
#define SAFE_STD_INQ_RESP_LEN 36 /* other lengths lock up some devices */
|
||||
|
||||
|
||||
const char *
|
||||
sg_cmds_version()
|
||||
{
|
||||
return version_str;
|
||||
}
|
||||
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
static int pr2ws(const char * fmt, ...)
|
||||
__attribute__ ((format (printf, 1, 2)));
|
||||
#else
|
||||
static int pr2ws(const char * fmt, ...);
|
||||
#endif
|
||||
|
||||
|
||||
static int
|
||||
pr2ws(const char * fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
int n;
|
||||
|
||||
va_start(args, fmt);
|
||||
n = vfprintf(sg_warnings_strm ? sg_warnings_strm : stderr, fmt, args);
|
||||
va_end(args);
|
||||
return n;
|
||||
}
|
||||
|
||||
/* Returns file descriptor >= 0 if successful. If error in Unix returns
|
||||
negated errno. */
|
||||
int
|
||||
sg_cmds_open_device(const char * device_name, bool read_only, int verbose)
|
||||
{
|
||||
/* The following 2 lines are temporary. It is to avoid a NULL pointer
|
||||
* crash when an old utility is used with a newer library built after
|
||||
* the sg_warnings_strm cleanup */
|
||||
if (NULL == sg_warnings_strm)
|
||||
sg_warnings_strm = stderr;
|
||||
|
||||
return scsi_pt_open_device(device_name, read_only, verbose);
|
||||
}
|
||||
|
||||
/* Returns file descriptor >= 0 if successful. If error in Unix returns
|
||||
negated errno. */
|
||||
int
|
||||
sg_cmds_open_flags(const char * device_name, int flags, int verbose)
|
||||
{
|
||||
return scsi_pt_open_flags(device_name, flags, verbose);
|
||||
}
|
||||
|
||||
/* Returns 0 if successful. If error in Unix returns negated errno. */
|
||||
int
|
||||
sg_cmds_close_device(int device_fd)
|
||||
{
|
||||
return scsi_pt_close_device(device_fd);
|
||||
}
|
||||
|
||||
static const char * const pass_through_s = "pass-through";
|
||||
|
||||
static int
|
||||
sg_cmds_process_helper(const char * leadin, int mx_di_len, int resid,
|
||||
const unsigned char * sbp, int slen, bool noisy,
|
||||
int verbose, int * o_sense_cat)
|
||||
{
|
||||
int scat, got;
|
||||
bool n = false;
|
||||
bool check_data_in = false;
|
||||
char b[512];
|
||||
|
||||
scat = sg_err_category_sense(sbp, slen);
|
||||
switch (scat) {
|
||||
case SG_LIB_CAT_NOT_READY:
|
||||
case SG_LIB_CAT_INVALID_OP:
|
||||
case SG_LIB_CAT_ILLEGAL_REQ:
|
||||
case SG_LIB_CAT_ABORTED_COMMAND:
|
||||
case SG_LIB_CAT_COPY_ABORTED:
|
||||
case SG_LIB_CAT_DATA_PROTECT:
|
||||
case SG_LIB_CAT_PROTECTION:
|
||||
case SG_LIB_CAT_NO_SENSE:
|
||||
case SG_LIB_CAT_MISCOMPARE:
|
||||
n = false;
|
||||
break;
|
||||
case SG_LIB_CAT_RECOVERED:
|
||||
case SG_LIB_CAT_MEDIUM_HARD:
|
||||
check_data_in = true;
|
||||
#if defined(__GNUC__)
|
||||
#if (__GNUC__ >= 7)
|
||||
__attribute__((fallthrough));
|
||||
/* FALL THROUGH */
|
||||
#endif
|
||||
#endif
|
||||
case SG_LIB_CAT_UNIT_ATTENTION:
|
||||
case SG_LIB_CAT_SENSE:
|
||||
default:
|
||||
n = noisy;
|
||||
break;
|
||||
}
|
||||
if (verbose || n) {
|
||||
if (leadin && (strlen(leadin) > 0))
|
||||
pr2ws("%s:\n", leadin);
|
||||
sg_get_sense_str(NULL, sbp, slen, (verbose > 1),
|
||||
sizeof(b), b);
|
||||
pr2ws("%s", b);
|
||||
if ((mx_di_len > 0) && (resid > 0)) {
|
||||
got = mx_di_len - resid;
|
||||
if ((verbose > 2) || check_data_in || (got > 0))
|
||||
pr2ws(" %s requested %d bytes (data-in) but got %d "
|
||||
"bytes\n", pass_through_s, mx_di_len, got);
|
||||
}
|
||||
}
|
||||
if (o_sense_cat)
|
||||
*o_sense_cat = scat;
|
||||
return -2;
|
||||
}
|
||||
|
||||
/* This is a helper function used by sg_cmds_* implementations after the
|
||||
* call to the pass-through. pt_res is returned from do_scsi_pt(). If valid
|
||||
* sense data is found it is decoded and output to sg_warnings_strm (def:
|
||||
* stderr); depending on the 'noisy' and 'verbose' settings. Returns -2 for
|
||||
* "sense" category (may not be fatal), -1 for failed, 0, or a positive
|
||||
* number. If 'mx_di_len > 0' then asks pass-through for resid and returns
|
||||
* (mx_di_len - resid); otherwise returns 0. So for data-in it should return
|
||||
* the actual number of bytes received. For data-out (to device) or no data
|
||||
* call with 'mx_di_len' set to 0 or less. If -2 returned then sense category
|
||||
* output via 'o_sense_cat' pointer (if not NULL). Note that several sense
|
||||
* categories also have data in bytes received; -2 is still returned. */
|
||||
int
|
||||
sg_cmds_process_resp(struct sg_pt_base * ptvp, const char * leadin,
|
||||
int pt_res, int mx_di_len, const unsigned char * sbp,
|
||||
bool noisy, int verbose, int * o_sense_cat)
|
||||
{
|
||||
int got, cat, duration, slen, resid, resp_code, sstat;
|
||||
bool transport_sense;
|
||||
char b[1024];
|
||||
|
||||
if (NULL == leadin)
|
||||
leadin = "";
|
||||
if (pt_res < 0) {
|
||||
#ifdef SG_LIB_LINUX
|
||||
if (verbose)
|
||||
pr2ws("%s: %s os error: %s\n", leadin, pass_through_s,
|
||||
safe_strerror(-pt_res));
|
||||
if ((-ENXIO == pt_res) && o_sense_cat) {
|
||||
if (verbose > 2)
|
||||
pr2ws("map ENXIO to SG_LIB_CAT_NOT_READY\n");
|
||||
*o_sense_cat = SG_LIB_CAT_NOT_READY;
|
||||
return -2;
|
||||
} else if (noisy && (0 == verbose))
|
||||
pr2ws("%s: %s os error: %s\n", leadin, pass_through_s,
|
||||
safe_strerror(-pt_res));
|
||||
#else
|
||||
if (noisy || verbose)
|
||||
pr2ws("%s: %s os error: %s\n", leadin, pass_through_s,
|
||||
safe_strerror(-pt_res));
|
||||
#endif
|
||||
return -1;
|
||||
} else if (SCSI_PT_DO_BAD_PARAMS == pt_res) {
|
||||
pr2ws("%s: bad %s setup\n", leadin, pass_through_s);
|
||||
return -1;
|
||||
} else if (SCSI_PT_DO_TIMEOUT == pt_res) {
|
||||
pr2ws("%s: %s timeout\n", leadin, pass_through_s);
|
||||
return -1;
|
||||
}
|
||||
if ((verbose > 2) && ((duration = get_scsi_pt_duration_ms(ptvp)) >= 0))
|
||||
pr2ws(" duration=%d ms\n", duration);
|
||||
resid = (mx_di_len > 0) ? get_scsi_pt_resid(ptvp) : 0;
|
||||
slen = get_scsi_pt_sense_len(ptvp);
|
||||
switch ((cat = get_scsi_pt_result_category(ptvp))) {
|
||||
case SCSI_PT_RESULT_GOOD:
|
||||
if (sbp && (slen > 7)) {
|
||||
resp_code = sbp[0] & 0x7f;
|
||||
/* SBC referrals can have status=GOOD and sense_key=COMPLETED */
|
||||
if (resp_code >= 0x70) {
|
||||
if (resp_code < 0x72) {
|
||||
if (SPC_SK_NO_SENSE != (0xf & sbp[2]))
|
||||
sg_err_category_sense(sbp, slen);
|
||||
} else if (resp_code < 0x74) {
|
||||
if (SPC_SK_NO_SENSE != (0xf & sbp[1]))
|
||||
sg_err_category_sense(sbp, slen);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mx_di_len > 0) {
|
||||
got = mx_di_len - resid;
|
||||
if ((verbose > 1) && (resid != 0))
|
||||
pr2ws(" %s: %s requested %d bytes (data-in) but got %d "
|
||||
"bytes\n", leadin, pass_through_s, mx_di_len, got);
|
||||
if (got >= 0)
|
||||
return got;
|
||||
else {
|
||||
if (verbose)
|
||||
pr2ws(" %s: %s can't get negative bytes, say it got "
|
||||
"none\n", leadin, pass_through_s);
|
||||
return 0;
|
||||
}
|
||||
} else
|
||||
return 0;
|
||||
case SCSI_PT_RESULT_STATUS: /* other than GOOD and CHECK CONDITION */
|
||||
sstat = get_scsi_pt_status_response(ptvp);
|
||||
if (o_sense_cat) {
|
||||
switch (sstat) {
|
||||
case SAM_STAT_RESERVATION_CONFLICT:
|
||||
*o_sense_cat = SG_LIB_CAT_RES_CONFLICT;
|
||||
return -2;
|
||||
case SAM_STAT_CONDITION_MET:
|
||||
*o_sense_cat = SG_LIB_CAT_CONDITION_MET;
|
||||
return -2;
|
||||
case SAM_STAT_BUSY:
|
||||
*o_sense_cat = SG_LIB_CAT_BUSY;
|
||||
return -2;
|
||||
case SAM_STAT_TASK_SET_FULL:
|
||||
*o_sense_cat = SG_LIB_CAT_TS_FULL;
|
||||
return -2;
|
||||
case SAM_STAT_ACA_ACTIVE:
|
||||
*o_sense_cat = SG_LIB_CAT_ACA_ACTIVE;
|
||||
return -2;
|
||||
case SAM_STAT_TASK_ABORTED:
|
||||
*o_sense_cat = SG_LIB_CAT_TASK_ABORTED;
|
||||
return -2;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (verbose || noisy) {
|
||||
sg_get_scsi_status_str(sstat, sizeof(b), b);
|
||||
pr2ws("%s: scsi status: %s\n", leadin, b);
|
||||
}
|
||||
return -1;
|
||||
case SCSI_PT_RESULT_SENSE:
|
||||
return sg_cmds_process_helper(leadin, mx_di_len, resid, sbp, slen,
|
||||
noisy, verbose, o_sense_cat);
|
||||
case SCSI_PT_RESULT_TRANSPORT_ERR:
|
||||
if (verbose || noisy) {
|
||||
get_scsi_pt_transport_err_str(ptvp, sizeof(b), b);
|
||||
pr2ws("%s: transport: %s\n", leadin, b);
|
||||
}
|
||||
#ifdef SG_LIB_LINUX
|
||||
transport_sense = (slen > 0);
|
||||
#else
|
||||
transport_sense = ((SAM_STAT_CHECK_CONDITION ==
|
||||
get_scsi_pt_status_response(ptvp)) && (slen > 0));
|
||||
#endif
|
||||
if (transport_sense)
|
||||
return sg_cmds_process_helper(leadin, mx_di_len, resid, sbp,
|
||||
slen, noisy, verbose, o_sense_cat);
|
||||
else
|
||||
return -1;
|
||||
case SCSI_PT_RESULT_OS_ERR:
|
||||
if (verbose || noisy) {
|
||||
get_scsi_pt_os_err_str(ptvp, sizeof(b), b);
|
||||
pr2ws("%s: os: %s\n", leadin, b);
|
||||
}
|
||||
return -1;
|
||||
default:
|
||||
pr2ws("%s: unknown %s result category (%d)\n", leadin, pass_through_s,
|
||||
cat);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
sg_cmds_is_nvme(const struct sg_pt_base * ptvp)
|
||||
{
|
||||
return pt_device_is_nvme(ptvp);
|
||||
}
|
||||
|
||||
static struct sg_pt_base *
|
||||
create_pt_obj(const char * cname)
|
||||
{
|
||||
struct sg_pt_base * ptvp = construct_scsi_pt_obj();
|
||||
if (NULL == ptvp)
|
||||
pr2ws("%s: out of memory\n", cname);
|
||||
return ptvp;
|
||||
}
|
||||
|
||||
static const char * const inquiry_s = "inquiry";
|
||||
|
||||
static int
|
||||
sg_ll_inquiry_com(int sg_fd, bool cmddt, bool evpd, int pg_op, void * resp,
|
||||
int mx_resp_len, int timeout_secs, int * residp,
|
||||
bool noisy, int verbose)
|
||||
{
|
||||
int res, ret, k, sense_cat, resid;
|
||||
unsigned char inq_cdb[INQUIRY_CMDLEN] = {INQUIRY_CMD, 0, 0, 0, 0, 0};
|
||||
unsigned char sense_b[SENSE_BUFF_LEN];
|
||||
unsigned char * up;
|
||||
struct sg_pt_base * ptvp;
|
||||
|
||||
if (cmddt)
|
||||
inq_cdb[1] |= 0x2;
|
||||
if (evpd)
|
||||
inq_cdb[1] |= 0x1;
|
||||
inq_cdb[2] = (unsigned char)pg_op;
|
||||
/* 16 bit allocation length (was 8, increased in spc3r09, 200209) */
|
||||
sg_put_unaligned_be16((uint16_t)mx_resp_len, inq_cdb + 3);
|
||||
if (verbose) {
|
||||
pr2ws(" %s cdb: ", inquiry_s);
|
||||
for (k = 0; k < INQUIRY_CMDLEN; ++k)
|
||||
pr2ws("%02x ", inq_cdb[k]);
|
||||
pr2ws("\n");
|
||||
}
|
||||
if (resp && (mx_resp_len > 0)) {
|
||||
up = (unsigned char *)resp;
|
||||
up[0] = 0x7f; /* defensive prefill */
|
||||
if (mx_resp_len > 4)
|
||||
up[4] = 0;
|
||||
}
|
||||
if (timeout_secs <= 0)
|
||||
timeout_secs = DEF_PT_TIMEOUT;
|
||||
ptvp = construct_scsi_pt_obj();
|
||||
if (NULL == ptvp) {
|
||||
pr2ws("%s: out of memory\n", __func__);
|
||||
if (residp)
|
||||
*residp = 0;
|
||||
return -1;
|
||||
}
|
||||
set_scsi_pt_cdb(ptvp, inq_cdb, sizeof(inq_cdb));
|
||||
set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
|
||||
set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len);
|
||||
res = do_scsi_pt(ptvp, sg_fd, timeout_secs, verbose);
|
||||
ret = sg_cmds_process_resp(ptvp, inquiry_s, res, mx_resp_len, sense_b,
|
||||
noisy, verbose, &sense_cat);
|
||||
resid = get_scsi_pt_resid(ptvp);
|
||||
if (residp)
|
||||
*residp = resid;
|
||||
if (-1 == ret)
|
||||
ret = sg_convert_errno(get_scsi_pt_os_err(ptvp));
|
||||
else if (-2 == ret) {
|
||||
switch (sense_cat) {
|
||||
case SG_LIB_CAT_RECOVERED:
|
||||
case SG_LIB_CAT_NO_SENSE:
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
ret = sense_cat;
|
||||
break;
|
||||
}
|
||||
} else if (ret < 4) {
|
||||
if (verbose)
|
||||
pr2ws("%s: got too few bytes (%d)\n", __func__, ret);
|
||||
ret = SG_LIB_CAT_MALFORMED;
|
||||
} else
|
||||
ret = 0;
|
||||
destruct_scsi_pt_obj(ptvp);
|
||||
|
||||
if (resid > 0) {
|
||||
if (resid > mx_resp_len) {
|
||||
pr2ws("%s resid (%d) should never exceed requested "
|
||||
"len=%d\n", inquiry_s, resid, mx_resp_len);
|
||||
return ret ? ret : SG_LIB_CAT_MALFORMED;
|
||||
}
|
||||
/* zero unfilled section of response buffer, based on resid */
|
||||
memset((unsigned char *)resp + (mx_resp_len - resid), 0, resid);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Invokes a SCSI INQUIRY command and yields the response. Returns 0 when
|
||||
* successful, various SG_LIB_CAT_* positive values or -1 -> other errors.
|
||||
* The CMDDT field is obsolete in the INQUIRY cdb. */
|
||||
int
|
||||
sg_ll_inquiry(int sg_fd, bool cmddt, bool evpd, int pg_op, void * resp,
|
||||
int mx_resp_len, bool noisy, int verbose)
|
||||
{
|
||||
return sg_ll_inquiry_com(sg_fd, cmddt, evpd, pg_op, resp, mx_resp_len,
|
||||
0 /* timeout_sec */, NULL, noisy, verbose);
|
||||
}
|
||||
|
||||
/* Yields most of first 36 bytes of a standard INQUIRY (evpd==0) response.
|
||||
* Returns 0 when successful, various SG_LIB_CAT_* positive values or
|
||||
* -1 -> other errors */
|
||||
int
|
||||
sg_simple_inquiry(int sg_fd, struct sg_simple_inquiry_resp * inq_data,
|
||||
bool noisy, int verbose)
|
||||
{
|
||||
int ret;
|
||||
unsigned char inq_resp[SAFE_STD_INQ_RESP_LEN];
|
||||
|
||||
if (inq_data) {
|
||||
memset(inq_data, 0, sizeof(* inq_data));
|
||||
inq_data->peripheral_qualifier = 0x3;
|
||||
inq_data->peripheral_type = 0x1f;
|
||||
}
|
||||
ret = sg_ll_inquiry_com(sg_fd, false, false, 0, inq_resp,
|
||||
sizeof(inq_resp), 0, NULL, noisy, verbose);
|
||||
|
||||
if (inq_data && (0 == ret)) {
|
||||
inq_data->peripheral_qualifier = (inq_resp[0] >> 5) & 0x7;
|
||||
inq_data->peripheral_type = inq_resp[0] & 0x1f;
|
||||
inq_data->byte_1 = inq_resp[1];
|
||||
inq_data->version = inq_resp[2];
|
||||
inq_data->byte_3 = inq_resp[3];
|
||||
inq_data->byte_5 = inq_resp[5];
|
||||
inq_data->byte_6 = inq_resp[6];
|
||||
inq_data->byte_7 = inq_resp[7];
|
||||
memcpy(inq_data->vendor, inq_resp + 8, 8);
|
||||
memcpy(inq_data->product, inq_resp + 16, 16);
|
||||
memcpy(inq_data->revision, inq_resp + 32, 4);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Invokes a SCSI INQUIRY command and yields the response. Returns 0 when
|
||||
* successful, various SG_LIB_CAT_* positive values or -1 -> other errors.
|
||||
* The CMDDT field is obsolete in the INQUIRY cdb (since spc3r16 in 2003) so
|
||||
* an argument to set it has been removed (use the REPORT SUPPORTED OPERATION
|
||||
* CODES command instead). Adds the ability to set the command abort timeout
|
||||
* and the ability to report the residual count. If timeout_secs is zero
|
||||
* or less the default command abort timeout (60 seconds) is used.
|
||||
* If residp is non-NULL then the residual value is written where residp
|
||||
* points. A residual value of 0 implies mx_resp_len bytes have be written
|
||||
* where resp points. If the residual value equals mx_resp_len then no
|
||||
* bytes have been written. */
|
||||
int
|
||||
sg_ll_inquiry_v2(int sg_fd, bool evpd, int pg_op, void * resp,
|
||||
int mx_resp_len, int timeout_secs, int * residp,
|
||||
bool noisy, int verbose)
|
||||
{
|
||||
return sg_ll_inquiry_com(sg_fd, false, evpd, pg_op, resp, mx_resp_len,
|
||||
timeout_secs, residp, noisy, verbose);
|
||||
}
|
||||
|
||||
/* Invokes a SCSI TEST UNIT READY command.
|
||||
* 'pack_id' is just for diagnostics, safe to set to 0.
|
||||
* Looks for progress indicator if 'progress' non-NULL;
|
||||
* if found writes value [0..65535] else write -1.
|
||||
* Returns 0 when successful, various SG_LIB_CAT_* positive values or
|
||||
* -1 -> other errors */
|
||||
int
|
||||
sg_ll_test_unit_ready_progress(int sg_fd, int pack_id, int * progress,
|
||||
bool noisy, int verbose)
|
||||
{
|
||||
static const char * const tur_s = "test unit ready";
|
||||
int res, ret, k, sense_cat;
|
||||
unsigned char tur_cdb[TUR_CMDLEN] = {TUR_CMD, 0, 0, 0, 0, 0};
|
||||
unsigned char sense_b[SENSE_BUFF_LEN];
|
||||
struct sg_pt_base * ptvp;
|
||||
|
||||
if (verbose) {
|
||||
pr2ws(" %s cdb: ", tur_s);
|
||||
for (k = 0; k < TUR_CMDLEN; ++k)
|
||||
pr2ws("%02x ", tur_cdb[k]);
|
||||
pr2ws("\n");
|
||||
}
|
||||
|
||||
if (NULL == ((ptvp = create_pt_obj(tur_s))))
|
||||
return -1;
|
||||
set_scsi_pt_cdb(ptvp, tur_cdb, sizeof(tur_cdb));
|
||||
set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
|
||||
set_scsi_pt_packet_id(ptvp, pack_id);
|
||||
res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
|
||||
ret = sg_cmds_process_resp(ptvp, tur_s, res, SG_NO_DATA_IN, sense_b,
|
||||
noisy, verbose, &sense_cat);
|
||||
if (-1 == ret) {
|
||||
int os_err = get_scsi_pt_os_err(ptvp);
|
||||
|
||||
if ((os_err > 0) && (os_err < 47))
|
||||
ret = SG_LIB_OS_BASE_ERR + os_err;
|
||||
} else if (-2 == ret) {
|
||||
if (progress) {
|
||||
int slen = get_scsi_pt_sense_len(ptvp);
|
||||
|
||||
if (! sg_get_sense_progress_fld(sense_b, slen, progress))
|
||||
*progress = -1;
|
||||
}
|
||||
switch (sense_cat) {
|
||||
case SG_LIB_CAT_RECOVERED:
|
||||
case SG_LIB_CAT_NO_SENSE:
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
ret = sense_cat;
|
||||
break;
|
||||
}
|
||||
} else
|
||||
ret = 0;
|
||||
|
||||
destruct_scsi_pt_obj(ptvp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Invokes a SCSI TEST UNIT READY command.
|
||||
* 'pack_id' is just for diagnostics, safe to set to 0.
|
||||
* Returns 0 when successful, various SG_LIB_CAT_* positive values or
|
||||
* -1 -> other errors */
|
||||
int
|
||||
sg_ll_test_unit_ready(int sg_fd, int pack_id, bool noisy, int verbose)
|
||||
{
|
||||
return sg_ll_test_unit_ready_progress(sg_fd, pack_id, NULL, noisy,
|
||||
verbose);
|
||||
}
|
||||
|
||||
/* Invokes a SCSI REQUEST SENSE command. Returns 0 when successful, various
|
||||
* SG_LIB_CAT_* positive values or -1 -> other errors */
|
||||
int
|
||||
sg_ll_request_sense(int sg_fd, bool desc, void * resp, int mx_resp_len,
|
||||
bool noisy, int verbose)
|
||||
{
|
||||
static const char * const rq_s = "request sense";
|
||||
int k, ret, res, sense_cat;
|
||||
unsigned char rs_cdb[REQUEST_SENSE_CMDLEN] =
|
||||
{REQUEST_SENSE_CMD, 0, 0, 0, 0, 0};
|
||||
unsigned char sense_b[SENSE_BUFF_LEN];
|
||||
struct sg_pt_base * ptvp;
|
||||
|
||||
if (desc)
|
||||
rs_cdb[1] |= 0x1;
|
||||
if (mx_resp_len > 0xff) {
|
||||
pr2ws("mx_resp_len cannot exceed 255\n");
|
||||
return -1;
|
||||
}
|
||||
rs_cdb[4] = mx_resp_len & 0xff;
|
||||
if (verbose) {
|
||||
pr2ws(" %s cmd: ", rq_s);
|
||||
for (k = 0; k < REQUEST_SENSE_CMDLEN; ++k)
|
||||
pr2ws("%02x ", rs_cdb[k]);
|
||||
pr2ws("\n");
|
||||
}
|
||||
|
||||
if (NULL == ((ptvp = create_pt_obj(rq_s))))
|
||||
return -1;
|
||||
set_scsi_pt_cdb(ptvp, rs_cdb, sizeof(rs_cdb));
|
||||
set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
|
||||
set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len);
|
||||
res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
|
||||
ret = sg_cmds_process_resp(ptvp, rq_s, res, mx_resp_len, sense_b, noisy,
|
||||
verbose, &sense_cat);
|
||||
if (-1 == ret) {
|
||||
int os_err = get_scsi_pt_os_err(ptvp);
|
||||
|
||||
if ((os_err > 0) && (os_err < 47))
|
||||
ret = SG_LIB_OS_BASE_ERR + os_err;
|
||||
} else if (-2 == ret) {
|
||||
switch (sense_cat) {
|
||||
case SG_LIB_CAT_RECOVERED:
|
||||
case SG_LIB_CAT_NO_SENSE:
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
ret = sense_cat;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if ((mx_resp_len >= 8) && (ret < 8)) {
|
||||
if (verbose)
|
||||
pr2ws(" %s: got %d bytes in response, too short\n", rq_s,
|
||||
ret);
|
||||
ret = -1;
|
||||
} else
|
||||
ret = 0;
|
||||
}
|
||||
destruct_scsi_pt_obj(ptvp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Invokes a SCSI REPORT LUNS command. Return of 0 -> success,
|
||||
* various SG_LIB_CAT_* positive values or -1 -> other errors */
|
||||
int
|
||||
sg_ll_report_luns(int sg_fd, int select_report, void * resp, int mx_resp_len,
|
||||
bool noisy, int verbose)
|
||||
{
|
||||
static const char * const report_luns_s = "report luns";
|
||||
int k, ret, res, sense_cat;
|
||||
unsigned char rl_cdb[REPORT_LUNS_CMDLEN] =
|
||||
{REPORT_LUNS_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
unsigned char sense_b[SENSE_BUFF_LEN];
|
||||
struct sg_pt_base * ptvp;
|
||||
|
||||
rl_cdb[2] = select_report & 0xff;
|
||||
sg_put_unaligned_be32((uint32_t)mx_resp_len, rl_cdb + 6);
|
||||
if (verbose) {
|
||||
pr2ws(" %s cdb: ", report_luns_s);
|
||||
for (k = 0; k < REPORT_LUNS_CMDLEN; ++k)
|
||||
pr2ws("%02x ", rl_cdb[k]);
|
||||
pr2ws("\n");
|
||||
}
|
||||
|
||||
if (NULL == ((ptvp = create_pt_obj(report_luns_s))))
|
||||
return -1;
|
||||
set_scsi_pt_cdb(ptvp, rl_cdb, sizeof(rl_cdb));
|
||||
set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
|
||||
set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len);
|
||||
res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
|
||||
ret = sg_cmds_process_resp(ptvp, report_luns_s, res, mx_resp_len,
|
||||
sense_b, noisy, verbose, &sense_cat);
|
||||
if (-1 == ret) {
|
||||
int os_err = get_scsi_pt_os_err(ptvp);
|
||||
|
||||
if ((os_err > 0) && (os_err < 47))
|
||||
ret = SG_LIB_OS_BASE_ERR + os_err;
|
||||
} else if (-2 == ret) {
|
||||
switch (sense_cat) {
|
||||
case SG_LIB_CAT_RECOVERED:
|
||||
case SG_LIB_CAT_NO_SENSE:
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
ret = sense_cat;
|
||||
break;
|
||||
}
|
||||
} else
|
||||
ret = 0;
|
||||
destruct_scsi_pt_obj(ptvp);
|
||||
return ret;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,382 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2018 Douglas Gilbert.
|
||||
* All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style
|
||||
* license that can be found in the BSD_LICENSE file.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#define __STDC_FORMAT_MACROS 1
|
||||
#include <inttypes.h>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "sg_lib.h"
|
||||
#include "sg_cmds_basic.h"
|
||||
#include "sg_cmds_mmc.h"
|
||||
#include "sg_pt.h"
|
||||
#include "sg_unaligned.h"
|
||||
|
||||
|
||||
#define SENSE_BUFF_LEN 64 /* Arbitrary, could be larger */
|
||||
|
||||
#define DEF_PT_TIMEOUT 60 /* 60 seconds */
|
||||
|
||||
#define GET_CONFIG_CMD 0x46
|
||||
#define GET_CONFIG_CMD_LEN 10
|
||||
#define GET_PERFORMANCE_CMD 0xac
|
||||
#define GET_PERFORMANCE_CMD_LEN 12
|
||||
#define SET_CD_SPEED_CMD 0xbb
|
||||
#define SET_CD_SPEED_CMDLEN 12
|
||||
#define SET_STREAMING_CMD 0xb6
|
||||
#define SET_STREAMING_CMDLEN 12
|
||||
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
static int pr2ws(const char * fmt, ...)
|
||||
__attribute__ ((format (printf, 1, 2)));
|
||||
#else
|
||||
static int pr2ws(const char * fmt, ...);
|
||||
#endif
|
||||
|
||||
|
||||
static int
|
||||
pr2ws(const char * fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
int n;
|
||||
|
||||
va_start(args, fmt);
|
||||
n = vfprintf(sg_warnings_strm ? sg_warnings_strm : stderr, fmt, args);
|
||||
va_end(args);
|
||||
return n;
|
||||
}
|
||||
|
||||
static struct sg_pt_base *
|
||||
create_pt_obj(const char * cname)
|
||||
{
|
||||
struct sg_pt_base * ptvp = construct_scsi_pt_obj();
|
||||
if (NULL == ptvp)
|
||||
pr2ws("%s: out of memory\n", cname);
|
||||
return ptvp;
|
||||
}
|
||||
|
||||
/* Invokes a SCSI SET CD SPEED command (MMC).
|
||||
* Return of 0 -> success, SG_LIB_CAT_INVALID_OP -> command not supported,
|
||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
|
||||
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
|
||||
* -1 -> other failure */
|
||||
int
|
||||
sg_ll_set_cd_speed(int sg_fd, int rot_control, int drv_read_speed,
|
||||
int drv_write_speed, bool noisy, int verbose)
|
||||
{
|
||||
static const char * const cdb_name_s = "set cd speed";
|
||||
int res, ret, k, sense_cat;
|
||||
unsigned char scsCmdBlk[SET_CD_SPEED_CMDLEN] = {SET_CD_SPEED_CMD, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0 ,0};
|
||||
unsigned char sense_b[SENSE_BUFF_LEN];
|
||||
struct sg_pt_base * ptvp;
|
||||
|
||||
scsCmdBlk[1] |= (rot_control & 0x3);
|
||||
sg_put_unaligned_be16((uint16_t)drv_read_speed, scsCmdBlk + 2);
|
||||
sg_put_unaligned_be16((uint16_t)drv_write_speed, scsCmdBlk + 4);
|
||||
|
||||
if (verbose) {
|
||||
pr2ws(" %s cdb: ", cdb_name_s);
|
||||
for (k = 0; k < SET_CD_SPEED_CMDLEN; ++k)
|
||||
pr2ws("%02x ", scsCmdBlk[k]);
|
||||
pr2ws("\n");
|
||||
}
|
||||
if (NULL == ((ptvp = create_pt_obj(cdb_name_s))))
|
||||
return -1;
|
||||
set_scsi_pt_cdb(ptvp, scsCmdBlk, sizeof(scsCmdBlk));
|
||||
set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
|
||||
res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
|
||||
ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, SG_NO_DATA_IN, sense_b,
|
||||
noisy, verbose, &sense_cat);
|
||||
if (-1 == ret) {
|
||||
int os_err = get_scsi_pt_os_err(ptvp);
|
||||
|
||||
if ((os_err > 0) && (os_err < 47))
|
||||
ret = SG_LIB_OS_BASE_ERR + os_err;
|
||||
} else if (-2 == ret) {
|
||||
switch (sense_cat) {
|
||||
case SG_LIB_CAT_NOT_READY:
|
||||
case SG_LIB_CAT_UNIT_ATTENTION:
|
||||
case SG_LIB_CAT_INVALID_OP:
|
||||
case SG_LIB_CAT_ILLEGAL_REQ:
|
||||
case SG_LIB_CAT_ABORTED_COMMAND:
|
||||
ret = sense_cat;
|
||||
break;
|
||||
case SG_LIB_CAT_RECOVERED:
|
||||
case SG_LIB_CAT_NO_SENSE:
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
} else
|
||||
ret = 0;
|
||||
|
||||
destruct_scsi_pt_obj(ptvp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Invokes a SCSI GET CONFIGURATION command (MMC-3,4,5).
|
||||
* Returns 0 when successful, SG_LIB_CAT_INVALID_OP if command not
|
||||
* supported, SG_LIB_CAT_ILLEGAL_REQ if field in cdb not supported,
|
||||
* SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND, else -1 */
|
||||
int
|
||||
sg_ll_get_config(int sg_fd, int rt, int starting, void * resp,
|
||||
int mx_resp_len, bool noisy, int verbose)
|
||||
{
|
||||
static const char * const cdb_name_s = "get configuration";
|
||||
int res, k, ret, sense_cat;
|
||||
unsigned char gcCmdBlk[GET_CONFIG_CMD_LEN] = {GET_CONFIG_CMD, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0};
|
||||
unsigned char sense_b[SENSE_BUFF_LEN];
|
||||
struct sg_pt_base * ptvp;
|
||||
|
||||
if ((rt < 0) || (rt > 3)) {
|
||||
pr2ws("Bad rt value: %d\n", rt);
|
||||
return -1;
|
||||
}
|
||||
gcCmdBlk[1] = (rt & 0x3);
|
||||
if ((starting < 0) || (starting > 0xffff)) {
|
||||
pr2ws("Bad starting field number: 0x%x\n", starting);
|
||||
return -1;
|
||||
}
|
||||
sg_put_unaligned_be16((uint16_t)starting, gcCmdBlk + 2);
|
||||
if ((mx_resp_len < 0) || (mx_resp_len > 0xffff)) {
|
||||
pr2ws("Bad mx_resp_len: 0x%x\n", starting);
|
||||
return -1;
|
||||
}
|
||||
sg_put_unaligned_be16((uint16_t)mx_resp_len, gcCmdBlk + 7);
|
||||
|
||||
if (verbose) {
|
||||
pr2ws(" %s cdb: ", cdb_name_s);
|
||||
for (k = 0; k < GET_CONFIG_CMD_LEN; ++k)
|
||||
pr2ws("%02x ", gcCmdBlk[k]);
|
||||
pr2ws("\n");
|
||||
}
|
||||
|
||||
if (NULL == ((ptvp = create_pt_obj(cdb_name_s))))
|
||||
return -1;
|
||||
set_scsi_pt_cdb(ptvp, gcCmdBlk, sizeof(gcCmdBlk));
|
||||
set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
|
||||
set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len);
|
||||
res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
|
||||
ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, mx_resp_len,
|
||||
sense_b, noisy, verbose, &sense_cat);
|
||||
if (-1 == ret) {
|
||||
int os_err = get_scsi_pt_os_err(ptvp);
|
||||
|
||||
if ((os_err > 0) && (os_err < 47))
|
||||
ret = SG_LIB_OS_BASE_ERR + os_err;
|
||||
} else if (-2 == ret) {
|
||||
switch (sense_cat) {
|
||||
case SG_LIB_CAT_INVALID_OP:
|
||||
case SG_LIB_CAT_ILLEGAL_REQ:
|
||||
case SG_LIB_CAT_UNIT_ATTENTION:
|
||||
case SG_LIB_CAT_ABORTED_COMMAND:
|
||||
ret = sense_cat;
|
||||
break;
|
||||
case SG_LIB_CAT_RECOVERED:
|
||||
case SG_LIB_CAT_NO_SENSE:
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if ((verbose > 2) && (ret > 3)) {
|
||||
unsigned char * bp;
|
||||
int len;
|
||||
|
||||
bp = (unsigned char *)resp;
|
||||
len = sg_get_unaligned_be32(bp + 0);
|
||||
if (len < 0)
|
||||
len = 0;
|
||||
len = (ret < len) ? ret : len;
|
||||
pr2ws(" %s: response:\n", cdb_name_s);
|
||||
if (3 == verbose) {
|
||||
pr2ws("%s:\n", (len > 256 ? ", first 256 bytes" : ""));
|
||||
hex2stderr((const uint8_t *)resp, (len > 256 ? 256 : len),
|
||||
-1);
|
||||
} else {
|
||||
pr2ws(":\n");
|
||||
hex2stderr((const uint8_t *)resp, len, 0);
|
||||
}
|
||||
}
|
||||
ret = 0;
|
||||
}
|
||||
destruct_scsi_pt_obj(ptvp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Invokes a SCSI GET PERFORMANCE command (MMC-3...6).
|
||||
* Returns 0 when successful, SG_LIB_CAT_INVALID_OP if command not
|
||||
* supported, SG_LIB_CAT_ILLEGAL_REQ if field in cdb not supported,
|
||||
* SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND, else -1 */
|
||||
int
|
||||
sg_ll_get_performance(int sg_fd, int data_type, unsigned int starting_lba,
|
||||
int max_num_desc, int ttype, void * resp,
|
||||
int mx_resp_len, bool noisy, int verbose)
|
||||
{
|
||||
static const char * const cdb_name_s = "get performance";
|
||||
int res, k, ret, sense_cat;
|
||||
unsigned char gpCmdBlk[GET_PERFORMANCE_CMD_LEN] = {GET_PERFORMANCE_CMD, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
unsigned char sense_b[SENSE_BUFF_LEN];
|
||||
struct sg_pt_base * ptvp;
|
||||
|
||||
if ((data_type < 0) || (data_type > 0x1f)) {
|
||||
pr2ws("Bad data_type value: %d\n", data_type);
|
||||
return -1;
|
||||
}
|
||||
gpCmdBlk[1] = (data_type & 0x1f);
|
||||
sg_put_unaligned_be32((uint32_t)starting_lba, gpCmdBlk + 2);
|
||||
if ((max_num_desc < 0) || (max_num_desc > 0xffff)) {
|
||||
pr2ws("Bad max_num_desc: 0x%x\n", max_num_desc);
|
||||
return -1;
|
||||
}
|
||||
sg_put_unaligned_be16((uint16_t)max_num_desc, gpCmdBlk + 8);
|
||||
if ((ttype < 0) || (ttype > 0xff)) {
|
||||
pr2ws("Bad type: 0x%x\n", ttype);
|
||||
return -1;
|
||||
}
|
||||
gpCmdBlk[10] = (unsigned char)ttype;
|
||||
|
||||
if (verbose) {
|
||||
pr2ws(" %s cdb: ", cdb_name_s);
|
||||
for (k = 0; k < GET_PERFORMANCE_CMD_LEN; ++k)
|
||||
pr2ws("%02x ", gpCmdBlk[k]);
|
||||
pr2ws("\n");
|
||||
}
|
||||
|
||||
if (NULL == ((ptvp = create_pt_obj(cdb_name_s))))
|
||||
return -1;
|
||||
set_scsi_pt_cdb(ptvp, gpCmdBlk, sizeof(gpCmdBlk));
|
||||
set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
|
||||
set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len);
|
||||
res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
|
||||
ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, mx_resp_len, sense_b,
|
||||
noisy, verbose, &sense_cat);
|
||||
if (-1 == ret) {
|
||||
int os_err = get_scsi_pt_os_err(ptvp);
|
||||
|
||||
if ((os_err > 0) && (os_err < 47))
|
||||
ret = SG_LIB_OS_BASE_ERR + os_err;
|
||||
} else if (-2 == ret) {
|
||||
switch (sense_cat) {
|
||||
case SG_LIB_CAT_INVALID_OP:
|
||||
case SG_LIB_CAT_ILLEGAL_REQ:
|
||||
case SG_LIB_CAT_UNIT_ATTENTION:
|
||||
case SG_LIB_CAT_ABORTED_COMMAND:
|
||||
ret = sense_cat;
|
||||
break;
|
||||
case SG_LIB_CAT_RECOVERED:
|
||||
case SG_LIB_CAT_NO_SENSE:
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if ((verbose > 2) && (ret > 3)) {
|
||||
unsigned char * bp;
|
||||
int len;
|
||||
|
||||
bp = (unsigned char *)resp;
|
||||
len = sg_get_unaligned_be32(bp + 0);
|
||||
if (len < 0)
|
||||
len = 0;
|
||||
len = (ret < len) ? ret : len;
|
||||
pr2ws(" %s: response", cdb_name_s);
|
||||
if (3 == verbose) {
|
||||
pr2ws("%s:\n", (len > 256 ? ", first 256 bytes" : ""));
|
||||
hex2stderr((const uint8_t *)resp, (len > 256 ? 256 : len),
|
||||
-1);
|
||||
} else {
|
||||
pr2ws(":\n");
|
||||
hex2stderr((const uint8_t *)resp, len, 0);
|
||||
}
|
||||
}
|
||||
ret = 0;
|
||||
}
|
||||
destruct_scsi_pt_obj(ptvp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Invokes a SCSI SET STREAMING command (MMC). Return of 0 -> success,
|
||||
* SG_LIB_CAT_INVALID_OP -> Set Streaming not supported,
|
||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND,
|
||||
* SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_NOT_READY -> device not ready,
|
||||
* -1 -> other failure */
|
||||
int
|
||||
sg_ll_set_streaming(int sg_fd, int type, void * paramp, int param_len,
|
||||
bool noisy, int verbose)
|
||||
{
|
||||
static const char * const cdb_name_s = "set streaming";
|
||||
int k, res, ret, sense_cat;
|
||||
unsigned char ssCmdBlk[SET_STREAMING_CMDLEN] =
|
||||
{SET_STREAMING_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
unsigned char sense_b[SENSE_BUFF_LEN];
|
||||
struct sg_pt_base * ptvp;
|
||||
|
||||
ssCmdBlk[8] = type;
|
||||
sg_put_unaligned_be16((uint16_t)param_len, ssCmdBlk + 9);
|
||||
if (verbose) {
|
||||
pr2ws(" %s cdb: ", cdb_name_s);
|
||||
for (k = 0; k < SET_STREAMING_CMDLEN; ++k)
|
||||
pr2ws("%02x ", ssCmdBlk[k]);
|
||||
pr2ws("\n");
|
||||
if ((verbose > 1) && paramp && param_len) {
|
||||
pr2ws(" %s parameter list:\n", cdb_name_s);
|
||||
hex2stderr((const uint8_t *)paramp, param_len, -1);
|
||||
}
|
||||
}
|
||||
|
||||
if (NULL == ((ptvp = create_pt_obj(cdb_name_s))))
|
||||
return -1;
|
||||
set_scsi_pt_cdb(ptvp, ssCmdBlk, sizeof(ssCmdBlk));
|
||||
set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
|
||||
set_scsi_pt_data_out(ptvp, (unsigned char *)paramp, param_len);
|
||||
res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
|
||||
ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, SG_NO_DATA_IN, sense_b,
|
||||
noisy, verbose, &sense_cat);
|
||||
if (-1 == ret) {
|
||||
int os_err = get_scsi_pt_os_err(ptvp);
|
||||
|
||||
if ((os_err > 0) && (os_err < 47))
|
||||
ret = SG_LIB_OS_BASE_ERR + os_err;
|
||||
} else if (-2 == ret) {
|
||||
switch (sense_cat) {
|
||||
case SG_LIB_CAT_NOT_READY:
|
||||
case SG_LIB_CAT_INVALID_OP:
|
||||
case SG_LIB_CAT_ILLEGAL_REQ:
|
||||
case SG_LIB_CAT_UNIT_ATTENTION:
|
||||
case SG_LIB_CAT_ABORTED_COMMAND:
|
||||
ret = sense_cat;
|
||||
break;
|
||||
case SG_LIB_CAT_RECOVERED:
|
||||
case SG_LIB_CAT_NO_SENSE:
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
} else
|
||||
ret = 0;
|
||||
destruct_scsi_pt_obj(ptvp);
|
||||
return ret;
|
||||
}
|
@ -1,256 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 1999-2018 Douglas Gilbert.
|
||||
* All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style
|
||||
* license that can be found in the BSD_LICENSE file.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef SG_LIB_LINUX
|
||||
|
||||
#include "sg_io_linux.h"
|
||||
|
||||
|
||||
/* Version 1.07 20160405 */
|
||||
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
static int pr2ws(const char * fmt, ...)
|
||||
__attribute__ ((format (printf, 1, 2)));
|
||||
#else
|
||||
static int pr2ws(const char * fmt, ...);
|
||||
#endif
|
||||
|
||||
|
||||
static int
|
||||
pr2ws(const char * fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
int n;
|
||||
|
||||
va_start(args, fmt);
|
||||
n = vfprintf(sg_warnings_strm ? sg_warnings_strm : stderr, fmt, args);
|
||||
va_end(args);
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
sg_print_masked_status(int masked_status)
|
||||
{
|
||||
int scsi_status = (masked_status << 1) & 0x7e;
|
||||
|
||||
sg_print_scsi_status(scsi_status);
|
||||
}
|
||||
|
||||
static const char * linux_host_bytes[] = {
|
||||
"DID_OK", "DID_NO_CONNECT", "DID_BUS_BUSY", "DID_TIME_OUT",
|
||||
"DID_BAD_TARGET", "DID_ABORT", "DID_PARITY", "DID_ERROR",
|
||||
"DID_RESET", "DID_BAD_INTR", "DID_PASSTHROUGH", "DID_SOFT_ERROR",
|
||||
"DID_IMM_RETRY", "DID_REQUEUE", "DID_TRANSPORT_DISRUPTED",
|
||||
"DID_TRANSPORT_FAILFAST", "DID_TARGET_FAILURE", "DID_NEXUS_FAILURE",
|
||||
"DID_ALLOC_FAILURE", "DID_MEDIUM_ERROR",
|
||||
};
|
||||
|
||||
#define LINUX_HOST_BYTES_SZ \
|
||||
(int)(sizeof(linux_host_bytes) / sizeof(linux_host_bytes[0]))
|
||||
|
||||
void
|
||||
sg_print_host_status(int host_status)
|
||||
{
|
||||
pr2ws("Host_status=0x%02x ", host_status);
|
||||
if ((host_status < 0) || (host_status >= LINUX_HOST_BYTES_SZ))
|
||||
pr2ws("is invalid ");
|
||||
else
|
||||
pr2ws("[%s] ", linux_host_bytes[host_status]);
|
||||
}
|
||||
|
||||
static const char * linux_driver_bytes[] = {
|
||||
"DRIVER_OK", "DRIVER_BUSY", "DRIVER_SOFT", "DRIVER_MEDIA",
|
||||
"DRIVER_ERROR", "DRIVER_INVALID", "DRIVER_TIMEOUT", "DRIVER_HARD",
|
||||
"DRIVER_SENSE"
|
||||
};
|
||||
|
||||
#define LINUX_DRIVER_BYTES_SZ \
|
||||
(int)(sizeof(linux_driver_bytes) / sizeof(linux_driver_bytes[0]))
|
||||
|
||||
#if 0
|
||||
static const char * linux_driver_suggests[] = {
|
||||
"SUGGEST_OK", "SUGGEST_RETRY", "SUGGEST_ABORT", "SUGGEST_REMAP",
|
||||
"SUGGEST_DIE", "UNKNOWN","UNKNOWN","UNKNOWN",
|
||||
"SUGGEST_SENSE"
|
||||
};
|
||||
|
||||
#define LINUX_DRIVER_SUGGESTS_SZ \
|
||||
(int)(sizeof(linux_driver_suggests) / sizeof(linux_driver_suggests[0]))
|
||||
#endif
|
||||
|
||||
|
||||
void
|
||||
sg_print_driver_status(int driver_status)
|
||||
{
|
||||
int driv;
|
||||
const char * driv_cp = "invalid";
|
||||
|
||||
driv = driver_status & SG_LIB_DRIVER_MASK;
|
||||
if (driv < LINUX_DRIVER_BYTES_SZ)
|
||||
driv_cp = linux_driver_bytes[driv];
|
||||
#if 0
|
||||
sugg = (driver_status & SG_LIB_SUGGEST_MASK) >> 4;
|
||||
if (sugg < LINUX_DRIVER_SUGGESTS_SZ)
|
||||
sugg_cp = linux_driver_suggests[sugg];
|
||||
#endif
|
||||
pr2ws("Driver_status=0x%02x", driver_status);
|
||||
pr2ws(" [%s] ", driv_cp);
|
||||
}
|
||||
|
||||
/* Returns 1 if no errors found and thus nothing printed; otherwise
|
||||
prints error/warning (prefix by 'leadin') and returns 0. */
|
||||
static int
|
||||
sg_linux_sense_print(const char * leadin, int scsi_status, int host_status,
|
||||
int driver_status, const unsigned char * sense_buffer,
|
||||
int sb_len, bool raw_sinfo)
|
||||
{
|
||||
bool done_leadin = false;
|
||||
bool done_sense = false;
|
||||
|
||||
scsi_status &= 0x7e; /*sanity */
|
||||
if ((0 == scsi_status) && (0 == host_status) && (0 == driver_status))
|
||||
return 1; /* No problems */
|
||||
if (0 != scsi_status) {
|
||||
if (leadin)
|
||||
pr2ws("%s: ", leadin);
|
||||
done_leadin = true;
|
||||
pr2ws("SCSI status: ");
|
||||
sg_print_scsi_status(scsi_status);
|
||||
pr2ws("\n");
|
||||
if (sense_buffer && ((scsi_status == SAM_STAT_CHECK_CONDITION) ||
|
||||
(scsi_status == SAM_STAT_COMMAND_TERMINATED))) {
|
||||
/* SAM_STAT_COMMAND_TERMINATED is obsolete */
|
||||
sg_print_sense(0, sense_buffer, sb_len, raw_sinfo);
|
||||
done_sense = true;
|
||||
}
|
||||
}
|
||||
if (0 != host_status) {
|
||||
if (leadin && (! done_leadin))
|
||||
pr2ws("%s: ", leadin);
|
||||
if (done_leadin)
|
||||
pr2ws("plus...: ");
|
||||
else
|
||||
done_leadin = true;
|
||||
sg_print_host_status(host_status);
|
||||
pr2ws("\n");
|
||||
}
|
||||
if (0 != driver_status) {
|
||||
if (done_sense &&
|
||||
(SG_LIB_DRIVER_SENSE == (SG_LIB_DRIVER_MASK & driver_status)))
|
||||
return 0;
|
||||
if (leadin && (! done_leadin))
|
||||
pr2ws("%s: ", leadin);
|
||||
if (done_leadin)
|
||||
pr2ws("plus...: ");
|
||||
sg_print_driver_status(driver_status);
|
||||
pr2ws("\n");
|
||||
if (sense_buffer && (! done_sense) &&
|
||||
(SG_LIB_DRIVER_SENSE == (SG_LIB_DRIVER_MASK & driver_status)))
|
||||
sg_print_sense(0, sense_buffer, sb_len, raw_sinfo);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef SG_IO
|
||||
|
||||
bool
|
||||
sg_normalize_sense(const struct sg_io_hdr * hp,
|
||||
struct sg_scsi_sense_hdr * sshp)
|
||||
{
|
||||
if ((NULL == hp) || (0 == hp->sb_len_wr)) {
|
||||
if (sshp)
|
||||
memset(sshp, 0, sizeof(struct sg_scsi_sense_hdr));
|
||||
return 0;
|
||||
}
|
||||
return sg_scsi_normalize_sense(hp->sbp, hp->sb_len_wr, sshp);
|
||||
}
|
||||
|
||||
/* Returns 1 if no errors found and thus nothing printed; otherwise
|
||||
returns 0. */
|
||||
int
|
||||
sg_chk_n_print3(const char * leadin, struct sg_io_hdr * hp,
|
||||
bool raw_sinfo)
|
||||
{
|
||||
return sg_linux_sense_print(leadin, hp->status, hp->host_status,
|
||||
hp->driver_status, hp->sbp, hp->sb_len_wr,
|
||||
raw_sinfo);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Returns 1 if no errors found and thus nothing printed; otherwise
|
||||
returns 0. */
|
||||
int
|
||||
sg_chk_n_print(const char * leadin, int masked_status, int host_status,
|
||||
int driver_status, const unsigned char * sense_buffer,
|
||||
int sb_len, bool raw_sinfo)
|
||||
{
|
||||
int scsi_status = (masked_status << 1) & 0x7e;
|
||||
|
||||
return sg_linux_sense_print(leadin, scsi_status, host_status,
|
||||
driver_status, sense_buffer, sb_len,
|
||||
raw_sinfo);
|
||||
}
|
||||
|
||||
#ifdef SG_IO
|
||||
int
|
||||
sg_err_category3(struct sg_io_hdr * hp)
|
||||
{
|
||||
return sg_err_category_new(hp->status, hp->host_status,
|
||||
hp->driver_status, hp->sbp, hp->sb_len_wr);
|
||||
}
|
||||
#endif
|
||||
|
||||
int
|
||||
sg_err_category(int masked_status, int host_status, int driver_status,
|
||||
const unsigned char * sense_buffer, int sb_len)
|
||||
{
|
||||
int scsi_status = (masked_status << 1) & 0x7e;
|
||||
|
||||
return sg_err_category_new(scsi_status, host_status, driver_status,
|
||||
sense_buffer, sb_len);
|
||||
}
|
||||
|
||||
int
|
||||
sg_err_category_new(int scsi_status, int host_status, int driver_status,
|
||||
const unsigned char * sense_buffer, int sb_len)
|
||||
{
|
||||
int masked_driver_status = (SG_LIB_DRIVER_MASK & driver_status);
|
||||
|
||||
scsi_status &= 0x7e;
|
||||
if ((0 == scsi_status) && (0 == host_status) &&
|
||||
(0 == masked_driver_status))
|
||||
return SG_LIB_CAT_CLEAN;
|
||||
if ((SAM_STAT_CHECK_CONDITION == scsi_status) ||
|
||||
(SAM_STAT_COMMAND_TERMINATED == scsi_status) ||
|
||||
(SG_LIB_DRIVER_SENSE == masked_driver_status))
|
||||
return sg_err_category_sense(sense_buffer, sb_len);
|
||||
if (0 != host_status) {
|
||||
if ((SG_LIB_DID_NO_CONNECT == host_status) ||
|
||||
(SG_LIB_DID_BUS_BUSY == host_status) ||
|
||||
(SG_LIB_DID_TIME_OUT == host_status))
|
||||
return SG_LIB_CAT_TIMEOUT;
|
||||
if (SG_LIB_DID_NEXUS_FAILURE == host_status)
|
||||
return SG_LIB_CAT_RES_CONFLICT;
|
||||
}
|
||||
if (SG_LIB_DRIVER_TIMEOUT == masked_driver_status)
|
||||
return SG_LIB_CAT_TIMEOUT;
|
||||
return SG_LIB_CAT_OTHER;
|
||||
}
|
||||
|
||||
#endif /* if SG_LIB_LINUX defined */
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,141 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2018 Douglas Gilbert.
|
||||
* All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style
|
||||
* license that can be found in the BSD_LICENSE file.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#define __STDC_FORMAT_MACROS 1
|
||||
#include <inttypes.h>
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "sg_lib.h"
|
||||
#include "sg_pt.h"
|
||||
#include "sg_pt_nvme.h"
|
||||
|
||||
|
||||
static const char * scsi_pt_version_str = "3.03 20180115";
|
||||
|
||||
static const char * nvme_scsi_vendor_str = "NVMe ";
|
||||
|
||||
|
||||
const char *
|
||||
scsi_pt_version()
|
||||
{
|
||||
return scsi_pt_version_str;
|
||||
}
|
||||
|
||||
/* Given the NVMe Identify controller response and optionally the NVMe
|
||||
* Identify namespace response (NULL otherwise), generate the SCSI VPD
|
||||
* page 0x83 (device identification) descriptor(s) in dop. Return the
|
||||
* number of bytes written which will not exceed max_do_len. Probably use
|
||||
* Peripheral Device Type (pdt) of 0 (disk) for don't know. Transport
|
||||
* protocol (tproto) should be -1 if not known, else SCSI value.
|
||||
* N.B. Does not write total VPD page length into dop[2:3] . */
|
||||
int
|
||||
sg_make_vpd_devid_for_nvme(const uint8_t * nvme_id_ctl_p,
|
||||
const uint8_t * nvme_id_ns_p, int pdt,
|
||||
int tproto, uint8_t * dop, int max_do_len)
|
||||
{
|
||||
bool have_nguid, have_eui64;
|
||||
int k, n;
|
||||
char b[4];
|
||||
|
||||
if ((NULL == nvme_id_ctl_p) || (NULL == dop) || (max_do_len < 56))
|
||||
return 0;
|
||||
|
||||
memset(dop, 0, max_do_len);
|
||||
dop[0] = 0x1f & pdt; /* (PQ=0)<<5 | (PDT=pdt); 0 or 0xd (SES) */
|
||||
dop[1] = 0x83; /* Device Identification VPD page number */
|
||||
/* Build a T10 Vendor ID based designator (desig_id=1) for controller */
|
||||
if (tproto >= 0) {
|
||||
dop[4] = ((0xf & tproto) << 4) | 0x2;
|
||||
dop[5] = 0xa1; /* PIV=1, ASSOC=2 (target device), desig_id=1 */
|
||||
} else {
|
||||
dop[4] = 0x2; /* Prococol id=0, code_set=2 (ASCII) */
|
||||
dop[5] = 0x21; /* PIV=0, ASSOC=2 (target device), desig_id=1 */
|
||||
}
|
||||
memcpy(dop + 8, nvme_scsi_vendor_str, 8); /* N.B. this is "NVMe " */
|
||||
memcpy(dop + 16, nvme_id_ctl_p + 24, 40); /* MN */
|
||||
for (k = 40; k > 0; --k) {
|
||||
if (' ' == dop[15 + k])
|
||||
dop[15 + k] = '_'; /* convert trailing spaces */
|
||||
else
|
||||
break;
|
||||
}
|
||||
if (40 == k)
|
||||
--k;
|
||||
n = 16 + 1 + k;
|
||||
if (max_do_len < (n + 20))
|
||||
return 0;
|
||||
memcpy(dop + n, nvme_id_ctl_p + 4, 20); /* SN */
|
||||
for (k = 20; k > 0; --k) { /* trim trailing spaces */
|
||||
if (' ' == dop[n + k - 1])
|
||||
dop[n + k - 1] = '\0';
|
||||
else
|
||||
break;
|
||||
}
|
||||
n += k;
|
||||
if (0 != (n % 4))
|
||||
n = ((n / 4) + 1) * 4; /* round up to next modulo 4 */
|
||||
dop[7] = n - 8;
|
||||
if (NULL == nvme_id_ns_p)
|
||||
return n;
|
||||
|
||||
/* Look for NGUID (16 byte identifier) or EUI64 (8 byte) fields in
|
||||
* NVME Identify for namespace. If found form a EUI and a SCSI string
|
||||
* descriptor for non-zero NGUID or EUI64 (prefer NGUID if both). */
|
||||
have_nguid = ! sg_all_zeros(nvme_id_ns_p + 104, 16);
|
||||
have_eui64 = ! sg_all_zeros(nvme_id_ns_p + 120, 8);
|
||||
if ((! have_nguid) && (! have_eui64))
|
||||
return n;
|
||||
if (have_nguid) {
|
||||
if (max_do_len < (n + 20))
|
||||
return n;
|
||||
dop[n + 0] = 0x1; /* Prococol id=0, code_set=1 (binary) */
|
||||
dop[n + 1] = 0x02; /* PIV=0, ASSOC=0 (lu), desig_id=2 (eui) */
|
||||
dop[n + 3] = 16;
|
||||
memcpy(dop + n + 4, nvme_id_ns_p + 104, 16);
|
||||
n += 20;
|
||||
if (max_do_len < (n + 40))
|
||||
return n;
|
||||
dop[n + 0] = 0x3; /* Prococol id=0, code_set=3 (utf8) */
|
||||
dop[n + 1] = 0x08; /* PIV=0, ASSOC=0 (lu), desig_id=8 (scsi string) */
|
||||
dop[n + 3] = 36;
|
||||
memcpy(dop + n + 4, "eui.", 4);
|
||||
for (k = 0; k < 16; ++k) {
|
||||
snprintf(b, sizeof(b), "%02X", nvme_id_ns_p[104 + k]);
|
||||
memcpy(dop + n + 8 + (2 * k), b, 2);
|
||||
}
|
||||
return n + 40;
|
||||
} else { /* have_eui64 is true, 8 byte identifier */
|
||||
if (max_do_len < (n + 12))
|
||||
return n;
|
||||
dop[n + 0] = 0x1; /* Prococol id=0, code_set=1 (binary) */
|
||||
dop[n + 1] = 0x02; /* PIV=0, ASSOC=0 (lu), desig_id=2 (eui) */
|
||||
dop[n + 3] = 8;
|
||||
memcpy(dop + n + 4, nvme_id_ns_p + 120, 8);
|
||||
n += 12;
|
||||
if (max_do_len < (n + 24))
|
||||
return n;
|
||||
dop[n + 0] = 0x3; /* Prococol id=0, code_set=3 (utf8) */
|
||||
dop[n + 1] = 0x08; /* PIV=0, ASSOC=0 (lu), desig_id=8 (scsi string) */
|
||||
dop[n + 3] = 20;
|
||||
memcpy(dop + n + 4, "eui.", 4);
|
||||
for (k = 0; k < 8; ++k) {
|
||||
snprintf(b, sizeof(b), "%02X", nvme_id_ns_p[120 + k]);
|
||||
memcpy(dop + n + 8 + (2 * k), b, 2);
|
||||
}
|
||||
return n + 24;
|
||||
}
|
||||
}
|
@ -1,964 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2005-2018 Douglas Gilbert.
|
||||
* All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style
|
||||
* license that can be found in the BSD_LICENSE file.
|
||||
*/
|
||||
|
||||
/* sg_pt_linux version 1.37 20180126 */
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/sysmacros.h> /* to define 'major' */
|
||||
#ifndef major
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <linux/major.h>
|
||||
|
||||
#include "sg_pt.h"
|
||||
#include "sg_lib.h"
|
||||
#include "sg_linux_inc.h"
|
||||
#include "sg_pt_linux.h"
|
||||
|
||||
|
||||
#ifdef major
|
||||
#define SG_DEV_MAJOR major
|
||||
#else
|
||||
#ifdef HAVE_LINUX_KDEV_T_H
|
||||
#include <linux/kdev_t.h>
|
||||
#endif
|
||||
#define SG_DEV_MAJOR MAJOR /* MAJOR() macro faulty if > 255 minors */
|
||||
#endif
|
||||
|
||||
#ifndef BLOCK_EXT_MAJOR
|
||||
#define BLOCK_EXT_MAJOR 259
|
||||
#endif
|
||||
|
||||
#define DEF_TIMEOUT 60000 /* 60,000 millisecs (60 seconds) */
|
||||
|
||||
static const char * linux_host_bytes[] = {
|
||||
"DID_OK", "DID_NO_CONNECT", "DID_BUS_BUSY", "DID_TIME_OUT",
|
||||
"DID_BAD_TARGET", "DID_ABORT", "DID_PARITY", "DID_ERROR",
|
||||
"DID_RESET", "DID_BAD_INTR", "DID_PASSTHROUGH", "DID_SOFT_ERROR",
|
||||
"DID_IMM_RETRY", "DID_REQUEUE" /* 0xd */,
|
||||
"DID_TRANSPORT_DISRUPTED", "DID_TRANSPORT_FAILFAST",
|
||||
"DID_TARGET_FAILURE" /* 0x10 */,
|
||||
"DID_NEXUS_FAILURE (reservation conflict)",
|
||||
"DID_ALLOC_FAILURE",
|
||||
"DID_MEDIUM_ERROR",
|
||||
};
|
||||
|
||||
#define LINUX_HOST_BYTES_SZ \
|
||||
(int)(sizeof(linux_host_bytes) / sizeof(linux_host_bytes[0]))
|
||||
|
||||
static const char * linux_driver_bytes[] = {
|
||||
"DRIVER_OK", "DRIVER_BUSY", "DRIVER_SOFT", "DRIVER_MEDIA",
|
||||
"DRIVER_ERROR", "DRIVER_INVALID", "DRIVER_TIMEOUT", "DRIVER_HARD",
|
||||
"DRIVER_SENSE"
|
||||
};
|
||||
|
||||
#define LINUX_DRIVER_BYTES_SZ \
|
||||
(int)(sizeof(linux_driver_bytes) / sizeof(linux_driver_bytes[0]))
|
||||
|
||||
#if 0
|
||||
static const char * linux_driver_suggests[] = {
|
||||
"SUGGEST_OK", "SUGGEST_RETRY", "SUGGEST_ABORT", "SUGGEST_REMAP",
|
||||
"SUGGEST_DIE", "UNKNOWN","UNKNOWN","UNKNOWN",
|
||||
"SUGGEST_SENSE"
|
||||
};
|
||||
|
||||
#define LINUX_DRIVER_SUGGESTS_SZ \
|
||||
(int)(sizeof(linux_driver_suggests) / sizeof(linux_driver_suggests[0]))
|
||||
#endif
|
||||
|
||||
/*
|
||||
* These defines are for constants that should be visible in the
|
||||
* /usr/include/scsi directory (brought in by sg_linux_inc.h).
|
||||
* Redefined and aliased here to decouple this code from
|
||||
* sg_io_linux.h N.B. the SUGGEST_* constants are no longer used.
|
||||
*/
|
||||
#ifndef DRIVER_MASK
|
||||
#define DRIVER_MASK 0x0f
|
||||
#endif
|
||||
#ifndef SUGGEST_MASK
|
||||
#define SUGGEST_MASK 0xf0
|
||||
#endif
|
||||
#ifndef DRIVER_SENSE
|
||||
#define DRIVER_SENSE 0x08
|
||||
#endif
|
||||
#define SG_LIB_DRIVER_MASK DRIVER_MASK
|
||||
#define SG_LIB_SUGGEST_MASK SUGGEST_MASK
|
||||
#define SG_LIB_DRIVER_SENSE DRIVER_SENSE
|
||||
|
||||
bool sg_bsg_nvme_char_major_checked = false;
|
||||
int sg_bsg_major = 0;
|
||||
volatile int sg_nvme_char_major = 0;
|
||||
|
||||
long sg_lin_page_size = 4096; /* default, overridden with correct value */
|
||||
|
||||
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
static int pr2ws(const char * fmt, ...)
|
||||
__attribute__ ((format (printf, 1, 2)));
|
||||
#else
|
||||
static int pr2ws(const char * fmt, ...);
|
||||
#endif
|
||||
|
||||
|
||||
static int
|
||||
pr2ws(const char * fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
int n;
|
||||
|
||||
va_start(args, fmt);
|
||||
n = vfprintf(sg_warnings_strm ? sg_warnings_strm : stderr, fmt, args);
|
||||
va_end(args);
|
||||
return n;
|
||||
}
|
||||
|
||||
/* This function only needs to be called once (unless a NVMe controller
|
||||
* can be hot-plugged into system in which case it should be called
|
||||
* (again) after that event). */
|
||||
void
|
||||
sg_find_bsg_nvme_char_major(int verbose)
|
||||
{
|
||||
bool got_one = false;
|
||||
int n;
|
||||
const char * proc_devices = "/proc/devices";
|
||||
char * cp;
|
||||
FILE *fp;
|
||||
char a[128];
|
||||
char b[128];
|
||||
|
||||
sg_lin_page_size = sysconf(_SC_PAGESIZE);
|
||||
if (NULL == (fp = fopen(proc_devices, "r"))) {
|
||||
if (verbose)
|
||||
pr2ws("fopen %s failed: %s\n", proc_devices, strerror(errno));
|
||||
return;
|
||||
}
|
||||
while ((cp = fgets(b, sizeof(b), fp))) {
|
||||
if ((1 == sscanf(b, "%126s", a)) &&
|
||||
(0 == memcmp(a, "Character", 9)))
|
||||
break;
|
||||
}
|
||||
while (cp && (cp = fgets(b, sizeof(b), fp))) {
|
||||
if (2 == sscanf(b, "%d %126s", &n, a)) {
|
||||
if (0 == strcmp("bsg", a)) {
|
||||
sg_bsg_major = n;
|
||||
if (got_one)
|
||||
break;
|
||||
got_one = true;
|
||||
} else if (0 == strcmp("nvme", a)) {
|
||||
sg_nvme_char_major = n;
|
||||
if (got_one)
|
||||
break;
|
||||
got_one = true;
|
||||
}
|
||||
} else
|
||||
break;
|
||||
}
|
||||
if (verbose > 3) {
|
||||
if (cp) {
|
||||
if (sg_bsg_major > 0)
|
||||
pr2ws("found sg_bsg_major=%d\n", sg_bsg_major);
|
||||
if (sg_nvme_char_major > 0)
|
||||
pr2ws("found sg_nvme_char_major=%d\n", sg_nvme_char_major);
|
||||
} else
|
||||
pr2ws("found no bsg not nvme char device in %s\n", proc_devices);
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
/* Assumes that sg_find_bsg_nvme_char_major() has already been called. Returns
|
||||
* true if dev_fd is a scsi generic pass-through device. If yields
|
||||
* *is_nvme_p = true with *nsid_p = 0 then dev_fd is a NVMe char device.
|
||||
* If yields *nsid_p > 0 then dev_fd is a NVMe block device. */
|
||||
static bool
|
||||
check_file_type(int dev_fd, struct stat * dev_statp, bool * is_bsg_p,
|
||||
bool * is_nvme_p, uint32_t * nsid_p, int * os_err_p,
|
||||
int verbose)
|
||||
{
|
||||
bool is_nvme = false;
|
||||
bool is_sg = false;
|
||||
bool is_bsg = false;
|
||||
bool is_block = false;
|
||||
int os_err = 0;
|
||||
int major_num;
|
||||
uint32_t nsid = 0; /* invalid NSID */
|
||||
|
||||
if (dev_fd >= 0) {
|
||||
if (fstat(dev_fd, dev_statp) < 0) {
|
||||
os_err = errno;
|
||||
if (verbose)
|
||||
pr2ws("%s: fstat() failed: %s (errno=%d)\n", __func__,
|
||||
safe_strerror(os_err), os_err);
|
||||
goto skip_out;
|
||||
}
|
||||
major_num = (int)SG_DEV_MAJOR(dev_statp->st_rdev);
|
||||
if (S_ISCHR(dev_statp->st_mode)) {
|
||||
if (SCSI_GENERIC_MAJOR == major_num)
|
||||
is_sg = true;
|
||||
else if (sg_bsg_major == major_num)
|
||||
is_bsg = true;
|
||||
else if (sg_nvme_char_major == major_num)
|
||||
is_nvme = true;
|
||||
} else if (S_ISBLK(dev_statp->st_mode)) {
|
||||
is_block = true;
|
||||
if (BLOCK_EXT_MAJOR == major_num) {
|
||||
is_nvme = true;
|
||||
nsid = ioctl(dev_fd, NVME_IOCTL_ID, NULL);
|
||||
if (SG_NVME_BROADCAST_NSID == nsid) { /* means ioctl error */
|
||||
os_err = errno;
|
||||
if (verbose)
|
||||
pr2ws("%s: ioctl(NVME_IOCTL_ID) failed: %s "
|
||||
"(errno=%d)\n", __func__, safe_strerror(os_err),
|
||||
os_err);
|
||||
} else
|
||||
os_err = 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
os_err = EBADF;
|
||||
if (verbose)
|
||||
pr2ws("%s: invalid file descriptor (%d)\n", __func__, dev_fd);
|
||||
}
|
||||
skip_out:
|
||||
if (verbose > 3) {
|
||||
pr2ws("%s: file descriptor is ", __func__);
|
||||
if (is_sg)
|
||||
pr2ws("sg device\n");
|
||||
else if (is_bsg)
|
||||
pr2ws("bsg device\n");
|
||||
else if (is_nvme && (0 == nsid))
|
||||
pr2ws("NVMe char device\n");
|
||||
else if (is_nvme)
|
||||
pr2ws("NVMe block device, nsid=%lld\n",
|
||||
((uint32_t)-1 == nsid) ? -1LL : (long long)nsid);
|
||||
else if (is_block)
|
||||
pr2ws("block device\n");
|
||||
else
|
||||
pr2ws("undetermined device, could be regular file\n");
|
||||
}
|
||||
if (is_bsg_p)
|
||||
*is_bsg_p = is_bsg;
|
||||
if (is_nvme_p)
|
||||
*is_nvme_p = is_nvme;
|
||||
if (nsid_p)
|
||||
*nsid_p = nsid;
|
||||
if (os_err_p)
|
||||
*os_err_p = os_err;
|
||||
return is_sg;
|
||||
}
|
||||
|
||||
/* Assumes dev_fd is an "open" file handle associated with device_name. If
|
||||
* the implementation (possibly for one OS) cannot determine from dev_fd if
|
||||
* a SCSI or NVMe pass-through is referenced, then it might guess based on
|
||||
* device_name. Returns 1 if SCSI generic pass-though device, returns 2 if
|
||||
* secondary SCSI pass-through device (in Linux a bsg device); returns 3 is
|
||||
* char NVMe device (i.e. no NSID); returns 4 if block NVMe device (includes
|
||||
* NSID), or 0 if something else (e.g. ATA block device) or dev_fd < 0.
|
||||
* If error, returns negated errno (operating system) value. */
|
||||
int
|
||||
check_pt_file_handle(int dev_fd, const char * device_name, int verbose)
|
||||
{
|
||||
if (verbose > 4)
|
||||
pr2ws("%s: dev_fd=%d, device_name: %s\n", __func__, dev_fd,
|
||||
device_name);
|
||||
/* Linux doesn't need device_name to determine which pass-through */
|
||||
if (! sg_bsg_nvme_char_major_checked) {
|
||||
sg_bsg_nvme_char_major_checked = true;
|
||||
sg_find_bsg_nvme_char_major(verbose);
|
||||
}
|
||||
if (dev_fd >= 0) {
|
||||
bool is_sg, is_bsg, is_nvme;
|
||||
int err;
|
||||
uint32_t nsid;
|
||||
struct stat a_stat;
|
||||
|
||||
is_sg = check_file_type(dev_fd, &a_stat, &is_bsg, &is_nvme, &nsid,
|
||||
&err, verbose);
|
||||
if (err)
|
||||
return -err;
|
||||
else if (is_sg)
|
||||
return 1;
|
||||
else if (is_bsg)
|
||||
return 2;
|
||||
else if (is_nvme && (0 == nsid))
|
||||
return 3;
|
||||
else if (is_nvme)
|
||||
return 4;
|
||||
else
|
||||
return 0;
|
||||
} else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* We make a runtime decision whether to use the sg v3 interface or the sg
|
||||
* v4 interface (currently exclusively used by the bsg driver). If all the
|
||||
* following are true we use sg v4 which is only currently supported on bsg
|
||||
* device nodes:
|
||||
* a) there is a bsg entry in the /proc/devices file
|
||||
* b) the device node given to scsi_pt_open() is a char device
|
||||
* c) the char major number of the device node given to scsi_pt_open()
|
||||
* matches the char major number of the bsg entry in /proc/devices
|
||||
* Otherwise the sg v3 interface is used.
|
||||
*
|
||||
* Note that in either case we prepare the data in a sg v4 structure. If
|
||||
* the runtime tests indicate that the v3 interface is needed then
|
||||
* do_scsi_pt_v3() transfers the input data into a v3 structure and
|
||||
* then the output data is transferred back into a sg v4 structure.
|
||||
* That implementation detail could change in the future.
|
||||
*
|
||||
* [20120806] Only use MAJOR() macro in kdev_t.h if that header file is
|
||||
* available and major() macro [N.B. lower case] is not available.
|
||||
*/
|
||||
|
||||
|
||||
#ifdef major
|
||||
#define SG_DEV_MAJOR major
|
||||
#else
|
||||
#ifdef HAVE_LINUX_KDEV_T_H
|
||||
#include <linux/kdev_t.h>
|
||||
#endif
|
||||
#define SG_DEV_MAJOR MAJOR /* MAJOR() macro faulty if > 255 minors */
|
||||
#endif
|
||||
|
||||
|
||||
/* Returns >= 0 if successful. If error in Unix returns negated errno. */
|
||||
int
|
||||
scsi_pt_open_device(const char * device_name, bool read_only, int verbose)
|
||||
{
|
||||
int oflags = O_NONBLOCK;
|
||||
|
||||
oflags |= (read_only ? O_RDONLY : O_RDWR);
|
||||
return scsi_pt_open_flags(device_name, oflags, verbose);
|
||||
}
|
||||
|
||||
/* Similar to scsi_pt_open_device() but takes Unix style open flags OR-ed */
|
||||
/* together. The 'flags' argument is advisory and may be ignored. */
|
||||
/* Returns >= 0 if successful, otherwise returns negated errno. */
|
||||
int
|
||||
scsi_pt_open_flags(const char * device_name, int flags, int verbose)
|
||||
{
|
||||
int fd;
|
||||
|
||||
if (! sg_bsg_nvme_char_major_checked) {
|
||||
sg_bsg_nvme_char_major_checked = true;
|
||||
sg_find_bsg_nvme_char_major(verbose);
|
||||
}
|
||||
if (verbose > 1) {
|
||||
pr2ws("open %s with flags=0x%x\n", device_name, flags);
|
||||
}
|
||||
fd = open(device_name, flags);
|
||||
if (fd < 0) {
|
||||
fd = -errno;
|
||||
if (verbose > 1)
|
||||
pr2ws("%s: open(%s, 0x%x) failed: %s\n", __func__, device_name,
|
||||
flags, safe_strerror(-fd));
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
/* Returns 0 if successful. If error in Unix returns negated errno. */
|
||||
int
|
||||
scsi_pt_close_device(int device_fd)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = close(device_fd);
|
||||
if (res < 0)
|
||||
res = -errno;
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/* Caller should additionally call get_scsi_pt_os_err() after this call */
|
||||
struct sg_pt_base *
|
||||
construct_scsi_pt_obj_with_fd(int dev_fd, int verbose)
|
||||
{
|
||||
int err;
|
||||
struct sg_pt_linux_scsi * ptp;
|
||||
|
||||
/* The following 2 lines are temporary. It is to avoid a NULL pointer
|
||||
* crash when an old utility is used with a newer library built after
|
||||
* the sg_warnings_strm cleanup */
|
||||
if (NULL == sg_warnings_strm)
|
||||
sg_warnings_strm = stderr;
|
||||
|
||||
ptp = (struct sg_pt_linux_scsi *)
|
||||
calloc(1, sizeof(struct sg_pt_linux_scsi));
|
||||
if (ptp) {
|
||||
err = set_pt_file_handle((struct sg_pt_base *)ptp, dev_fd, verbose);
|
||||
if ((0 == err) && (! ptp->is_nvme)) {
|
||||
ptp->io_hdr.guard = 'Q';
|
||||
#ifdef BSG_PROTOCOL_SCSI
|
||||
ptp->io_hdr.protocol = BSG_PROTOCOL_SCSI;
|
||||
#endif
|
||||
#ifdef BSG_SUB_PROTOCOL_SCSI_CMD
|
||||
ptp->io_hdr.subprotocol = BSG_SUB_PROTOCOL_SCSI_CMD;
|
||||
#endif
|
||||
}
|
||||
} else if (verbose)
|
||||
pr2ws("%s: calloc() failed, out of memory?\n", __func__);
|
||||
|
||||
return (struct sg_pt_base *)ptp;
|
||||
}
|
||||
|
||||
struct sg_pt_base *
|
||||
construct_scsi_pt_obj()
|
||||
{
|
||||
return construct_scsi_pt_obj_with_fd(-1 /* dev_fd */, 0 /* verbose */);
|
||||
}
|
||||
|
||||
void
|
||||
destruct_scsi_pt_obj(struct sg_pt_base * vp)
|
||||
{
|
||||
struct sg_pt_linux_scsi * ptp = &vp->impl;
|
||||
|
||||
if (ptp->free_nvme_id_ctlp) {
|
||||
free(ptp->free_nvme_id_ctlp);
|
||||
ptp->free_nvme_id_ctlp = NULL;
|
||||
ptp->nvme_id_ctlp = NULL;
|
||||
}
|
||||
if (ptp)
|
||||
free(ptp);
|
||||
}
|
||||
|
||||
/* Remembers previous device file descriptor */
|
||||
void
|
||||
clear_scsi_pt_obj(struct sg_pt_base * vp)
|
||||
{
|
||||
bool is_sg, is_bsg, is_nvme;
|
||||
int fd;
|
||||
uint32_t nvme_nsid;
|
||||
struct sg_pt_linux_scsi * ptp = &vp->impl;
|
||||
|
||||
if (ptp) {
|
||||
fd = ptp->dev_fd;
|
||||
is_sg = ptp->is_sg;
|
||||
is_bsg = ptp->is_bsg;
|
||||
is_nvme = ptp->is_nvme;
|
||||
nvme_nsid = ptp->nvme_nsid;
|
||||
memset(ptp, 0, sizeof(struct sg_pt_linux_scsi));
|
||||
ptp->io_hdr.guard = 'Q';
|
||||
#ifdef BSG_PROTOCOL_SCSI
|
||||
ptp->io_hdr.protocol = BSG_PROTOCOL_SCSI;
|
||||
#endif
|
||||
#ifdef BSG_SUB_PROTOCOL_SCSI_CMD
|
||||
ptp->io_hdr.subprotocol = BSG_SUB_PROTOCOL_SCSI_CMD;
|
||||
#endif
|
||||
ptp->dev_fd = fd;
|
||||
ptp->is_sg = is_sg;
|
||||
ptp->is_bsg = is_bsg;
|
||||
ptp->is_nvme = is_nvme;
|
||||
ptp->nvme_direct = false;
|
||||
ptp->nvme_nsid = nvme_nsid;
|
||||
}
|
||||
}
|
||||
|
||||
/* Forget any previous dev_fd and install the one given. May attempt to
|
||||
* find file type (e.g. if pass-though) from OS so there could be an error.
|
||||
* Returns 0 for success or the same value as get_scsi_pt_os_err()
|
||||
* will return. dev_fd should be >= 0 for a valid file handle or -1 . */
|
||||
int
|
||||
set_pt_file_handle(struct sg_pt_base * vp, int dev_fd, int verbose)
|
||||
{
|
||||
struct sg_pt_linux_scsi * ptp = &vp->impl;
|
||||
struct stat a_stat;
|
||||
|
||||
if (! sg_bsg_nvme_char_major_checked) {
|
||||
sg_bsg_nvme_char_major_checked = true;
|
||||
sg_find_bsg_nvme_char_major(verbose);
|
||||
}
|
||||
ptp->dev_fd = dev_fd;
|
||||
if (dev_fd >= 0)
|
||||
ptp->is_sg = check_file_type(dev_fd, &a_stat, &ptp->is_bsg,
|
||||
&ptp->is_nvme, &ptp->nvme_nsid,
|
||||
&ptp->os_err, verbose);
|
||||
else {
|
||||
ptp->is_sg = false;
|
||||
ptp->is_bsg = false;
|
||||
ptp->is_nvme = false;
|
||||
ptp->nvme_direct = false;
|
||||
ptp->nvme_nsid = 0;
|
||||
ptp->os_err = 0;
|
||||
}
|
||||
return ptp->os_err;
|
||||
}
|
||||
|
||||
/* Valid file handles (which is the return value) are >= 0 . Returns -1
|
||||
* if there is no valid file handle. */
|
||||
int
|
||||
get_pt_file_handle(const struct sg_pt_base * vp)
|
||||
{
|
||||
const struct sg_pt_linux_scsi * ptp = &vp->impl;
|
||||
|
||||
return ptp->dev_fd;
|
||||
}
|
||||
|
||||
void
|
||||
set_scsi_pt_cdb(struct sg_pt_base * vp, const unsigned char * cdb,
|
||||
int cdb_len)
|
||||
{
|
||||
struct sg_pt_linux_scsi * ptp = &vp->impl;
|
||||
|
||||
if (ptp->io_hdr.request)
|
||||
++ptp->in_err;
|
||||
ptp->io_hdr.request = (__u64)(sg_uintptr_t)cdb;
|
||||
ptp->io_hdr.request_len = cdb_len;
|
||||
}
|
||||
|
||||
void
|
||||
set_scsi_pt_sense(struct sg_pt_base * vp, unsigned char * sense,
|
||||
int max_sense_len)
|
||||
{
|
||||
struct sg_pt_linux_scsi * ptp = &vp->impl;
|
||||
|
||||
if (ptp->io_hdr.response)
|
||||
++ptp->in_err;
|
||||
memset(sense, 0, max_sense_len);
|
||||
ptp->io_hdr.response = (__u64)(sg_uintptr_t)sense;
|
||||
ptp->io_hdr.max_response_len = max_sense_len;
|
||||
}
|
||||
|
||||
/* Setup for data transfer from device */
|
||||
void
|
||||
set_scsi_pt_data_in(struct sg_pt_base * vp, unsigned char * dxferp,
|
||||
int dxfer_ilen)
|
||||
{
|
||||
struct sg_pt_linux_scsi * ptp = &vp->impl;
|
||||
|
||||
if (ptp->io_hdr.din_xferp)
|
||||
++ptp->in_err;
|
||||
if (dxfer_ilen > 0) {
|
||||
ptp->io_hdr.din_xferp = (__u64)(sg_uintptr_t)dxferp;
|
||||
ptp->io_hdr.din_xfer_len = dxfer_ilen;
|
||||
}
|
||||
}
|
||||
|
||||
/* Setup for data transfer toward device */
|
||||
void
|
||||
set_scsi_pt_data_out(struct sg_pt_base * vp, const unsigned char * dxferp,
|
||||
int dxfer_olen)
|
||||
{
|
||||
struct sg_pt_linux_scsi * ptp = &vp->impl;
|
||||
|
||||
if (ptp->io_hdr.dout_xferp)
|
||||
++ptp->in_err;
|
||||
if (dxfer_olen > 0) {
|
||||
ptp->io_hdr.dout_xferp = (__u64)(sg_uintptr_t)dxferp;
|
||||
ptp->io_hdr.dout_xfer_len = dxfer_olen;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
set_pt_metadata_xfer(struct sg_pt_base * vp, unsigned char * dxferp,
|
||||
uint32_t dxfer_len, bool out_true)
|
||||
{
|
||||
struct sg_pt_linux_scsi * ptp = &vp->impl;
|
||||
|
||||
if (dxfer_len > 0) {
|
||||
ptp->mdxferp = dxferp;
|
||||
ptp->mdxfer_len = dxfer_len;
|
||||
ptp->mdxfer_out = out_true;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
set_scsi_pt_packet_id(struct sg_pt_base * vp, int pack_id)
|
||||
{
|
||||
struct sg_pt_linux_scsi * ptp = &vp->impl;
|
||||
|
||||
ptp->io_hdr.spare_in = pack_id;
|
||||
}
|
||||
|
||||
void
|
||||
set_scsi_pt_tag(struct sg_pt_base * vp, uint64_t tag)
|
||||
{
|
||||
struct sg_pt_linux_scsi * ptp = &vp->impl;
|
||||
|
||||
ptp->io_hdr.request_tag = tag;
|
||||
}
|
||||
|
||||
/* Note that task management function codes are transport specific */
|
||||
void
|
||||
set_scsi_pt_task_management(struct sg_pt_base * vp, int tmf_code)
|
||||
{
|
||||
struct sg_pt_linux_scsi * ptp = &vp->impl;
|
||||
|
||||
ptp->io_hdr.subprotocol = 1; /* SCSI task management function */
|
||||
ptp->tmf_request[0] = (unsigned char)tmf_code; /* assume it fits */
|
||||
ptp->io_hdr.request = (__u64)(sg_uintptr_t)(&(ptp->tmf_request[0]));
|
||||
ptp->io_hdr.request_len = 1;
|
||||
}
|
||||
|
||||
void
|
||||
set_scsi_pt_task_attr(struct sg_pt_base * vp, int attribute, int priority)
|
||||
{
|
||||
struct sg_pt_linux_scsi * ptp = &vp->impl;
|
||||
|
||||
ptp->io_hdr.request_attr = attribute;
|
||||
ptp->io_hdr.request_priority = priority;
|
||||
}
|
||||
|
||||
#ifndef BSG_FLAG_Q_AT_TAIL
|
||||
#define BSG_FLAG_Q_AT_TAIL 0x10
|
||||
#endif
|
||||
#ifndef BSG_FLAG_Q_AT_HEAD
|
||||
#define BSG_FLAG_Q_AT_HEAD 0x20
|
||||
#endif
|
||||
|
||||
/* Need this later if translated to v3 interface */
|
||||
#ifndef SG_FLAG_Q_AT_TAIL
|
||||
#define SG_FLAG_Q_AT_TAIL 0x10
|
||||
#endif
|
||||
#ifndef SG_FLAG_Q_AT_HEAD
|
||||
#define SG_FLAG_Q_AT_HEAD 0x20
|
||||
#endif
|
||||
|
||||
void
|
||||
set_scsi_pt_flags(struct sg_pt_base * vp, int flags)
|
||||
{
|
||||
struct sg_pt_linux_scsi * ptp = &vp->impl;
|
||||
|
||||
/* default action of bsg driver (sg v4) is QUEUE_AT_HEAD */
|
||||
/* default action of block layer SG_IO ioctl is QUEUE_AT_TAIL */
|
||||
if (SCSI_PT_FLAGS_QUEUE_AT_HEAD & flags) { /* favour AT_HEAD */
|
||||
ptp->io_hdr.flags |= BSG_FLAG_Q_AT_HEAD;
|
||||
ptp->io_hdr.flags &= ~BSG_FLAG_Q_AT_TAIL;
|
||||
} else if (SCSI_PT_FLAGS_QUEUE_AT_TAIL & flags) {
|
||||
ptp->io_hdr.flags |= BSG_FLAG_Q_AT_TAIL;
|
||||
ptp->io_hdr.flags &= ~BSG_FLAG_Q_AT_HEAD;
|
||||
}
|
||||
}
|
||||
|
||||
/* N.B. Returns din_resid and ignores dout_resid */
|
||||
int
|
||||
get_scsi_pt_resid(const struct sg_pt_base * vp)
|
||||
{
|
||||
const struct sg_pt_linux_scsi * ptp = &vp->impl;
|
||||
|
||||
if (NULL == ptp)
|
||||
return 0;
|
||||
return ptp->nvme_direct ? 0 : ptp->io_hdr.din_resid;
|
||||
}
|
||||
|
||||
int
|
||||
get_scsi_pt_status_response(const struct sg_pt_base * vp)
|
||||
{
|
||||
const struct sg_pt_linux_scsi * ptp = &vp->impl;
|
||||
|
||||
if (NULL == ptp)
|
||||
return 0;
|
||||
return (int)(ptp->nvme_direct ? ptp->nvme_status :
|
||||
ptp->io_hdr.device_status);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
get_pt_result(const struct sg_pt_base * vp)
|
||||
{
|
||||
const struct sg_pt_linux_scsi * ptp = &vp->impl;
|
||||
|
||||
if (NULL == ptp)
|
||||
return 0;
|
||||
return ptp->nvme_direct ? ptp->nvme_result :
|
||||
ptp->io_hdr.device_status;
|
||||
}
|
||||
|
||||
int
|
||||
get_scsi_pt_sense_len(const struct sg_pt_base * vp)
|
||||
{
|
||||
const struct sg_pt_linux_scsi * ptp = &vp->impl;
|
||||
|
||||
return ptp->io_hdr.response_len;
|
||||
}
|
||||
|
||||
int
|
||||
get_scsi_pt_duration_ms(const struct sg_pt_base * vp)
|
||||
{
|
||||
const struct sg_pt_linux_scsi * ptp = &vp->impl;
|
||||
|
||||
return ptp->io_hdr.duration;
|
||||
}
|
||||
|
||||
int
|
||||
get_scsi_pt_transport_err(const struct sg_pt_base * vp)
|
||||
{
|
||||
const struct sg_pt_linux_scsi * ptp = &vp->impl;
|
||||
|
||||
return ptp->io_hdr.transport_status;
|
||||
}
|
||||
|
||||
void
|
||||
set_scsi_pt_transport_err(struct sg_pt_base * vp, int err)
|
||||
{
|
||||
struct sg_pt_linux_scsi * ptp = &vp->impl;
|
||||
|
||||
ptp->io_hdr.transport_status = err;
|
||||
}
|
||||
|
||||
/* Returns b which will contain a null char terminated string (if
|
||||
* max_b_len > 0). Combined driver and transport (called "host" in Linux
|
||||
* kernel) statuses */
|
||||
char *
|
||||
get_scsi_pt_transport_err_str(const struct sg_pt_base * vp, int max_b_len,
|
||||
char * b)
|
||||
{
|
||||
const struct sg_pt_linux_scsi * ptp = &vp->impl;
|
||||
int ds = ptp->io_hdr.driver_status;
|
||||
int hs = ptp->io_hdr.transport_status;
|
||||
int n, m;
|
||||
char * cp = b;
|
||||
int driv;
|
||||
const char * driv_cp = "invalid";
|
||||
|
||||
if (max_b_len < 1)
|
||||
return b;
|
||||
m = max_b_len;
|
||||
n = 0;
|
||||
if (hs) {
|
||||
if ((hs < 0) || (hs >= LINUX_HOST_BYTES_SZ))
|
||||
n = snprintf(cp, m, "Host_status=0x%02x is invalid\n", hs);
|
||||
else
|
||||
n = snprintf(cp, m, "Host_status=0x%02x [%s]\n", hs,
|
||||
linux_host_bytes[hs]);
|
||||
}
|
||||
m -= n;
|
||||
if (m < 1) {
|
||||
b[max_b_len - 1] = '\0';
|
||||
return b;
|
||||
}
|
||||
cp += n;
|
||||
driv = ds & SG_LIB_DRIVER_MASK;
|
||||
if (driv < LINUX_DRIVER_BYTES_SZ)
|
||||
driv_cp = linux_driver_bytes[driv];
|
||||
#if 0
|
||||
sugg = (ds & SG_LIB_SUGGEST_MASK) >> 4;
|
||||
if (sugg < LINUX_DRIVER_SUGGESTS_SZ)
|
||||
sugg_cp = linux_driver_suggests[sugg];
|
||||
#endif
|
||||
n = snprintf(cp, m, "Driver_status=0x%02x [%s]\n", ds, driv_cp);
|
||||
m -= n;
|
||||
if (m < 1)
|
||||
b[max_b_len - 1] = '\0';
|
||||
return b;
|
||||
}
|
||||
|
||||
int
|
||||
get_scsi_pt_result_category(const struct sg_pt_base * vp)
|
||||
{
|
||||
const struct sg_pt_linux_scsi * ptp = &vp->impl;
|
||||
int dr_st = ptp->io_hdr.driver_status & SG_LIB_DRIVER_MASK;
|
||||
int scsi_st = ptp->io_hdr.device_status & 0x7e;
|
||||
|
||||
if (ptp->os_err)
|
||||
return SCSI_PT_RESULT_OS_ERR;
|
||||
else if (ptp->io_hdr.transport_status)
|
||||
return SCSI_PT_RESULT_TRANSPORT_ERR;
|
||||
else if (dr_st && (SG_LIB_DRIVER_SENSE != dr_st))
|
||||
return SCSI_PT_RESULT_TRANSPORT_ERR;
|
||||
else if ((SG_LIB_DRIVER_SENSE == dr_st) ||
|
||||
(SAM_STAT_CHECK_CONDITION == scsi_st) ||
|
||||
(SAM_STAT_COMMAND_TERMINATED == scsi_st))
|
||||
return SCSI_PT_RESULT_SENSE;
|
||||
else if (scsi_st)
|
||||
return SCSI_PT_RESULT_STATUS;
|
||||
else
|
||||
return SCSI_PT_RESULT_GOOD;
|
||||
}
|
||||
|
||||
int
|
||||
get_scsi_pt_os_err(const struct sg_pt_base * vp)
|
||||
{
|
||||
const struct sg_pt_linux_scsi * ptp = &vp->impl;
|
||||
|
||||
return ptp->os_err;
|
||||
}
|
||||
|
||||
char *
|
||||
get_scsi_pt_os_err_str(const struct sg_pt_base * vp, int max_b_len, char * b)
|
||||
{
|
||||
const struct sg_pt_linux_scsi * ptp = &vp->impl;
|
||||
const char * cp;
|
||||
|
||||
cp = safe_strerror(ptp->os_err);
|
||||
strncpy(b, cp, max_b_len);
|
||||
if ((int)strlen(cp) >= max_b_len)
|
||||
b[max_b_len - 1] = '\0';
|
||||
return b;
|
||||
}
|
||||
|
||||
bool
|
||||
pt_device_is_nvme(const struct sg_pt_base * vp)
|
||||
{
|
||||
const struct sg_pt_linux_scsi * ptp = &vp->impl;
|
||||
|
||||
return ptp->is_nvme;
|
||||
}
|
||||
|
||||
/* If a NVMe block device (which includes the NSID) handle is associated
|
||||
* with 'vp', then its NSID is returned (values range from 0x1 to
|
||||
* 0xffffffe). Otherwise 0 is returned. */
|
||||
uint32_t
|
||||
get_pt_nvme_nsid(const struct sg_pt_base * vp)
|
||||
{
|
||||
const struct sg_pt_linux_scsi * ptp = &vp->impl;
|
||||
|
||||
return ptp->nvme_nsid;
|
||||
}
|
||||
|
||||
/* Executes SCSI command using sg v3 interface */
|
||||
static int
|
||||
do_scsi_pt_v3(struct sg_pt_linux_scsi * ptp, int fd, int time_secs,
|
||||
int verbose)
|
||||
{
|
||||
struct sg_io_hdr v3_hdr;
|
||||
|
||||
memset(&v3_hdr, 0, sizeof(v3_hdr));
|
||||
/* convert v4 to v3 header */
|
||||
v3_hdr.interface_id = 'S';
|
||||
v3_hdr.dxfer_direction = SG_DXFER_NONE;
|
||||
v3_hdr.cmdp = (unsigned char *)(long)ptp->io_hdr.request;
|
||||
v3_hdr.cmd_len = (unsigned char)ptp->io_hdr.request_len;
|
||||
if (ptp->io_hdr.din_xfer_len > 0) {
|
||||
if (ptp->io_hdr.dout_xfer_len > 0) {
|
||||
if (verbose)
|
||||
pr2ws("sgv3 doesn't support bidi\n");
|
||||
return SCSI_PT_DO_BAD_PARAMS;
|
||||
}
|
||||
v3_hdr.dxferp = (void *)(long)ptp->io_hdr.din_xferp;
|
||||
v3_hdr.dxfer_len = (unsigned int)ptp->io_hdr.din_xfer_len;
|
||||
v3_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
|
||||
} else if (ptp->io_hdr.dout_xfer_len > 0) {
|
||||
v3_hdr.dxferp = (void *)(long)ptp->io_hdr.dout_xferp;
|
||||
v3_hdr.dxfer_len = (unsigned int)ptp->io_hdr.dout_xfer_len;
|
||||
v3_hdr.dxfer_direction = SG_DXFER_TO_DEV;
|
||||
}
|
||||
if (ptp->io_hdr.response && (ptp->io_hdr.max_response_len > 0)) {
|
||||
v3_hdr.sbp = (unsigned char *)(long)ptp->io_hdr.response;
|
||||
v3_hdr.mx_sb_len = (unsigned char)ptp->io_hdr.max_response_len;
|
||||
}
|
||||
v3_hdr.pack_id = (int)ptp->io_hdr.spare_in;
|
||||
if (BSG_FLAG_Q_AT_HEAD & ptp->io_hdr.flags)
|
||||
v3_hdr.flags |= SG_FLAG_Q_AT_HEAD; /* favour AT_HEAD */
|
||||
else if (BSG_FLAG_Q_AT_TAIL & ptp->io_hdr.flags)
|
||||
v3_hdr.flags |= SG_FLAG_Q_AT_TAIL;
|
||||
|
||||
if (NULL == v3_hdr.cmdp) {
|
||||
if (verbose)
|
||||
pr2ws("No SCSI command (cdb) given\n");
|
||||
return SCSI_PT_DO_BAD_PARAMS;
|
||||
}
|
||||
/* io_hdr.timeout is in milliseconds, if greater than zero */
|
||||
v3_hdr.timeout = ((time_secs > 0) ? (time_secs * 1000) : DEF_TIMEOUT);
|
||||
/* Finally do the v3 SG_IO ioctl */
|
||||
if (ioctl(fd, SG_IO, &v3_hdr) < 0) {
|
||||
ptp->os_err = errno;
|
||||
if (verbose > 1)
|
||||
pr2ws("ioctl(SG_IO v3) failed: %s (errno=%d)\n",
|
||||
safe_strerror(ptp->os_err), ptp->os_err);
|
||||
return -ptp->os_err;
|
||||
}
|
||||
ptp->io_hdr.device_status = (__u32)v3_hdr.status;
|
||||
ptp->io_hdr.driver_status = (__u32)v3_hdr.driver_status;
|
||||
ptp->io_hdr.transport_status = (__u32)v3_hdr.host_status;
|
||||
ptp->io_hdr.response_len = (__u32)v3_hdr.sb_len_wr;
|
||||
ptp->io_hdr.duration = (__u32)v3_hdr.duration;
|
||||
ptp->io_hdr.din_resid = (__s32)v3_hdr.resid;
|
||||
/* v3_hdr.info not passed back since no mapping defined (yet) */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Executes SCSI command (or at least forwards it to lower layers).
|
||||
* Returns 0 for success, negative numbers are negated 'errno' values from
|
||||
* OS system calls. Positive return values are errors from this package. */
|
||||
int
|
||||
do_scsi_pt(struct sg_pt_base * vp, int fd, int time_secs, int verbose)
|
||||
{
|
||||
int err;
|
||||
struct sg_pt_linux_scsi * ptp = &vp->impl;
|
||||
bool have_checked_for_type = (ptp->dev_fd >= 0);
|
||||
|
||||
if (! sg_bsg_nvme_char_major_checked) {
|
||||
sg_bsg_nvme_char_major_checked = true;
|
||||
sg_find_bsg_nvme_char_major(verbose);
|
||||
}
|
||||
if (ptp->in_err) {
|
||||
if (verbose)
|
||||
pr2ws("Replicated or unused set_scsi_pt... functions\n");
|
||||
return SCSI_PT_DO_BAD_PARAMS;
|
||||
}
|
||||
if (fd >= 0) {
|
||||
if ((ptp->dev_fd >= 0) && (fd != ptp->dev_fd)) {
|
||||
if (verbose)
|
||||
pr2ws("%s: file descriptor given to create() and here "
|
||||
"differ\n", __func__);
|
||||
return SCSI_PT_DO_BAD_PARAMS;
|
||||
}
|
||||
ptp->dev_fd = fd;
|
||||
} else if (ptp->dev_fd < 0) {
|
||||
if (verbose)
|
||||
pr2ws("%s: invalid file descriptors\n", __func__);
|
||||
return SCSI_PT_DO_BAD_PARAMS;
|
||||
} else
|
||||
fd = ptp->dev_fd;
|
||||
if (! have_checked_for_type) {
|
||||
err = set_pt_file_handle(vp, ptp->dev_fd, verbose);
|
||||
if (err)
|
||||
return -ptp->os_err;
|
||||
}
|
||||
if (ptp->os_err)
|
||||
return -ptp->os_err;
|
||||
if (ptp->is_nvme)
|
||||
return sg_do_nvme_pt(vp, -1, time_secs, verbose);
|
||||
else if (sg_bsg_major <= 0)
|
||||
return do_scsi_pt_v3(ptp, fd, time_secs, verbose);
|
||||
else if (ptp->is_bsg)
|
||||
; /* drop through to sg v4 implementation */
|
||||
else
|
||||
return do_scsi_pt_v3(ptp, fd, time_secs, verbose);
|
||||
|
||||
if (! ptp->io_hdr.request) {
|
||||
if (verbose)
|
||||
pr2ws("No SCSI command (cdb) given (v4)\n");
|
||||
return SCSI_PT_DO_BAD_PARAMS;
|
||||
}
|
||||
/* io_hdr.timeout is in milliseconds */
|
||||
ptp->io_hdr.timeout = ((time_secs > 0) ? (time_secs * 1000) :
|
||||
DEF_TIMEOUT);
|
||||
#if 0
|
||||
/* sense buffer already zeroed */
|
||||
if (ptp->io_hdr.response && (ptp->io_hdr.max_response_len > 0)) {
|
||||
void * p;
|
||||
|
||||
p = (void *)(long)ptp->io_hdr.response;
|
||||
memset(p, 0, ptp->io_hdr.max_response_len);
|
||||
}
|
||||
#endif
|
||||
if (ioctl(fd, SG_IO, &ptp->io_hdr) < 0) {
|
||||
ptp->os_err = errno;
|
||||
if (verbose > 1)
|
||||
pr2ws("ioctl(SG_IO v4) failed: %s (errno=%d)\n",
|
||||
safe_strerror(ptp->os_err), ptp->os_err);
|
||||
return -ptp->os_err;
|
||||
}
|
||||
return 0;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,516 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2018 Luben Tuikov and Douglas Gilbert.
|
||||
* All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style
|
||||
* license that can be found in the BSD_LICENSE file.
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <getopt.h>
|
||||
#define __STDC_FORMAT_MACROS 1
|
||||
#include <inttypes.h>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
#include "sg_lib.h"
|
||||
#include "sg_cmds_basic.h"
|
||||
#include "sg_cmds_extra.h"
|
||||
#include "sg_unaligned.h"
|
||||
#include "sg_pr2serr.h"
|
||||
|
||||
#ifdef SG_LIB_WIN32
|
||||
#ifdef SG_LIB_WIN32_DIRECT
|
||||
#include "sg_pt.h" /* needed for scsi_pt_win32_direct() */
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This utility issues the SCSI WRITE BUFFER command to the given device.
|
||||
*/
|
||||
|
||||
static const char * version_str = "1.24 20180111"; /* spc5r18 */
|
||||
|
||||
#define ME "sg_write_buffer: "
|
||||
#define DEF_XFER_LEN (8 * 1024 * 1024)
|
||||
#define EBUFF_SZ 256
|
||||
|
||||
#define WRITE_BUFFER_CMD 0x3b
|
||||
#define WRITE_BUFFER_CMDLEN 10
|
||||
#define SENSE_BUFF_LEN 64 /* Arbitrary, could be larger */
|
||||
#define DEF_PT_TIMEOUT 300 /* 300 seconds, 5 minutes */
|
||||
|
||||
static struct option long_options[] = {
|
||||
{"bpw", required_argument, 0, 'b'},
|
||||
{"dry-run", no_argument, 0, 'd'},
|
||||
{"dry_run", no_argument, 0, 'd'},
|
||||
{"help", no_argument, 0, 'h'},
|
||||
{"id", required_argument, 0, 'i'},
|
||||
{"in", required_argument, 0, 'I'},
|
||||
{"length", required_argument, 0, 'l'},
|
||||
{"mode", required_argument, 0, 'm'},
|
||||
{"offset", required_argument, 0, 'o'},
|
||||
{"read-stdin", no_argument, 0, 'r'},
|
||||
{"read_stdin", no_argument, 0, 'r'},
|
||||
{"raw", no_argument, 0, 'r'},
|
||||
{"skip", required_argument, 0, 's'},
|
||||
{"specific", required_argument, 0, 'S'},
|
||||
{"timeout", required_argument, 0, 't' },
|
||||
{"verbose", no_argument, 0, 'v'},
|
||||
{"version", no_argument, 0, 'V'},
|
||||
{0, 0, 0, 0},
|
||||
};
|
||||
|
||||
|
||||
static void
|
||||
usage()
|
||||
{
|
||||
pr2serr("Usage: "
|
||||
"sg_write_buffer [--bpw=CS] [--dry-run] [--help] [--id=ID] "
|
||||
"[--in=FILE]\n"
|
||||
" [--length=LEN] [--mode=MO] "
|
||||
"[--offset=OFF]\n"
|
||||
" [--read-stdin] [--skip=SKIP] "
|
||||
"[--specific=MS]\n"
|
||||
" [--timeout=TO] [--verbose] [--version] "
|
||||
"DEVICE\n"
|
||||
" where:\n"
|
||||
" --bpw=CS|-b CS CS is chunk size: bytes per write "
|
||||
"buffer\n"
|
||||
" command (def: 0 -> as many as "
|
||||
"possible)\n"
|
||||
" --dry-run|-d skip WRITE BUFFER commands, do "
|
||||
"everything else\n"
|
||||
" --help|-h print out usage message then exit\n"
|
||||
" --id=ID|-i ID buffer identifier (0 (default) to "
|
||||
"255)\n"
|
||||
" --in=FILE|-I FILE read from FILE ('-I -' read "
|
||||
"from stdin)\n"
|
||||
" --length=LEN|-l LEN length in bytes to write; may be "
|
||||
"deduced from\n"
|
||||
" FILE\n"
|
||||
" --mode=MO|-m MO write buffer mode, MO is number or "
|
||||
"acronym\n"
|
||||
" (def: 0 -> 'combined header and "
|
||||
"data' (obs))\n"
|
||||
" --offset=OFF|-o OFF buffer offset (unit: bytes, def: 0)\n"
|
||||
" --read-stdin|-r read from stdin (same as '-I -')\n"
|
||||
" --skip=SKIP|-s SKIP bytes in file FILE to skip before "
|
||||
"reading\n"
|
||||
" --specific=MS|-S MS mode specific value; 3 bit field "
|
||||
"(0 to 7)\n"
|
||||
" --timeout=TO|-t TO command timeout in seconds (def: "
|
||||
"300)\n"
|
||||
" --verbose|-v increase verbosity\n"
|
||||
" --version|-V print version string and exit\n\n"
|
||||
"Performs one or more SCSI WRITE BUFFER commands. Use '-m xxx' "
|
||||
"to list\navailable modes. A chunk size of 4 KB ('--bpw=4k') "
|
||||
"seems to work well.\nExample: sg_write_buffer -b 4k -I xxx.lod "
|
||||
"-m 7 /dev/sg3\n"
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
#define MODE_HEADER_DATA 0
|
||||
#define MODE_VENDOR 1
|
||||
#define MODE_DATA 2
|
||||
#define MODE_DNLD_MC 4
|
||||
#define MODE_DNLD_MC_SAVE 5
|
||||
#define MODE_DNLD_MC_OFFS 6
|
||||
#define MODE_DNLD_MC_OFFS_SAVE 7
|
||||
#define MODE_ECHO_BUFFER 0x0A
|
||||
#define MODE_DNLD_MC_EV_OFFS_DEFER 0x0D
|
||||
#define MODE_DNLD_MC_OFFS_DEFER 0x0E
|
||||
#define MODE_ACTIVATE_MC 0x0F
|
||||
#define MODE_EN_EX_ECHO 0x1A
|
||||
#define MODE_DIS_EX 0x1B
|
||||
#define MODE_DNLD_ERR_HISTORY 0x1C
|
||||
|
||||
|
||||
struct mode_s {
|
||||
const char *mode_string;
|
||||
int mode;
|
||||
const char *comment;
|
||||
};
|
||||
|
||||
static struct mode_s mode_arr[] = {
|
||||
{"hd", MODE_HEADER_DATA, "combined header and data "
|
||||
"(obsolete)"},
|
||||
{"vendor", MODE_VENDOR, "vendor specific"},
|
||||
{"data", MODE_DATA, "data"},
|
||||
{"dmc", MODE_DNLD_MC, "download microcode and activate"},
|
||||
{"dmc_save", MODE_DNLD_MC_SAVE, "download microcode, save and "
|
||||
"activate"},
|
||||
{"dmc_offs", MODE_DNLD_MC_OFFS, "download microcode with offsets "
|
||||
"and activate"},
|
||||
{"dmc_offs_save", MODE_DNLD_MC_OFFS_SAVE, "download microcode with "
|
||||
"offsets, save and\n\t\t\t\tactivate"},
|
||||
{"echo", MODE_ECHO_BUFFER, "write data to echo buffer"},
|
||||
{"dmc_offs_ev_defer", MODE_DNLD_MC_EV_OFFS_DEFER, "download "
|
||||
"microcode with offsets, select\n\t\t\t\tactivation event, "
|
||||
"save and defer activation"},
|
||||
{"dmc_offs_defer", MODE_DNLD_MC_OFFS_DEFER, "download microcode "
|
||||
"with offsets, save and\n\t\t\t\tdefer activation"},
|
||||
{"activate_mc", MODE_ACTIVATE_MC, "activate deferred microcode"},
|
||||
{"en_ex", MODE_EN_EX_ECHO, "enable expander communications "
|
||||
"protocol and\n\t\t\t\techo buffer (obsolete)"},
|
||||
{"dis_ex", MODE_DIS_EX, "disable expander communications "
|
||||
"protocol\n\t\t\t\t(obsolete)"},
|
||||
{"deh", MODE_DNLD_ERR_HISTORY, "download application client "
|
||||
"error history "},
|
||||
{NULL, 0, NULL},
|
||||
};
|
||||
|
||||
static void
|
||||
print_modes(void)
|
||||
{
|
||||
const struct mode_s * mp;
|
||||
|
||||
pr2serr("The modes parameter argument can be numeric (hex or decimal)\n"
|
||||
"or symbolic:\n");
|
||||
for (mp = mode_arr; mp->mode_string; ++mp) {
|
||||
pr2serr(" %2d (0x%02x) %-18s%s\n", mp->mode, mp->mode,
|
||||
mp->mode_string, mp->comment);
|
||||
}
|
||||
pr2serr("\nAdditionally '--bpw=<val>,act' does a activate deferred "
|
||||
"microcode after\nsuccessful dmc_offs_defer and "
|
||||
"dmc_offs_ev_defer mode downloads.\n");
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main(int argc, char * argv[])
|
||||
{
|
||||
bool bpw_then_activate = false;
|
||||
bool dry_run = false;
|
||||
bool got_stdin = false;
|
||||
bool wb_len_given = false;
|
||||
int sg_fd, infd, res, c, len, k, n;
|
||||
int bpw = 0;
|
||||
int do_help = 0;
|
||||
int ret = 0;
|
||||
int verbose = 0;
|
||||
int wb_id = 0;
|
||||
int wb_len = 0;
|
||||
int wb_mode = 0;
|
||||
int wb_offset = 0;
|
||||
int wb_skip = 0;
|
||||
int wb_timeout = DEF_PT_TIMEOUT;
|
||||
int wb_mspec = 0;
|
||||
const char * device_name = NULL;
|
||||
const char * file_name = NULL;
|
||||
unsigned char * dop = NULL;
|
||||
char * cp;
|
||||
const struct mode_s * mp;
|
||||
char ebuff[EBUFF_SZ];
|
||||
|
||||
while (1) {
|
||||
int option_index = 0;
|
||||
|
||||
c = getopt_long(argc, argv, "b:dhi:I:l:m:o:rs:S:t:vV", long_options,
|
||||
&option_index);
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
switch (c) {
|
||||
case 'b':
|
||||
bpw = sg_get_num(optarg);
|
||||
if (bpw < 0) {
|
||||
pr2serr("argument to '--bpw' should be in a positive "
|
||||
"number\n");
|
||||
return SG_LIB_SYNTAX_ERROR;
|
||||
}
|
||||
if ((cp = strchr(optarg, ','))) {
|
||||
if (0 == strncmp("act", cp + 1, 3))
|
||||
bpw_then_activate = true;
|
||||
}
|
||||
break;
|
||||
case 'd':
|
||||
dry_run = true;
|
||||
break;
|
||||
case 'h':
|
||||
case '?':
|
||||
++do_help;
|
||||
break;
|
||||
case 'i':
|
||||
wb_id = sg_get_num(optarg);
|
||||
if ((wb_id < 0) || (wb_id > 255)) {
|
||||
pr2serr("argument to '--id' should be in the range 0 to "
|
||||
"255\n");
|
||||
return SG_LIB_SYNTAX_ERROR;
|
||||
}
|
||||
break;
|
||||
case 'I':
|
||||
file_name = optarg;
|
||||
break;
|
||||
case 'l':
|
||||
wb_len = sg_get_num(optarg);
|
||||
if (wb_len < 0) {
|
||||
pr2serr("bad argument to '--length'\n");
|
||||
return SG_LIB_SYNTAX_ERROR;
|
||||
}
|
||||
wb_len_given = true;
|
||||
break;
|
||||
case 'm':
|
||||
if (isdigit(*optarg)) {
|
||||
wb_mode = sg_get_num(optarg);
|
||||
if ((wb_mode < 0) || (wb_mode > 31)) {
|
||||
pr2serr("argument to '--mode' should be in the range 0 "
|
||||
"to 31\n");
|
||||
return SG_LIB_SYNTAX_ERROR;
|
||||
}
|
||||
} else {
|
||||
len = strlen(optarg);
|
||||
for (mp = mode_arr; mp->mode_string; ++mp) {
|
||||
if (0 == strncmp(mp->mode_string, optarg, len)) {
|
||||
wb_mode = mp->mode;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (! mp->mode_string) {
|
||||
print_modes();
|
||||
return SG_LIB_SYNTAX_ERROR;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'o':
|
||||
wb_offset = sg_get_num(optarg);
|
||||
if (wb_offset < 0) {
|
||||
pr2serr("bad argument to '--offset'\n");
|
||||
return SG_LIB_SYNTAX_ERROR;
|
||||
}
|
||||
break;
|
||||
case 'r': /* --read-stdin and --raw (previous name) */
|
||||
file_name = "-";
|
||||
break;
|
||||
case 's':
|
||||
wb_skip = sg_get_num(optarg);
|
||||
if (wb_skip < 0) {
|
||||
pr2serr("bad argument to '--skip'\n");
|
||||
return SG_LIB_SYNTAX_ERROR;
|
||||
}
|
||||
break;
|
||||
case 'S':
|
||||
wb_mspec = sg_get_num(optarg);
|
||||
if ((wb_mspec < 0) || (wb_mspec > 7)) {
|
||||
pr2serr("expected argument to '--specific' to be 0 to 7\n");
|
||||
return SG_LIB_SYNTAX_ERROR;
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
wb_timeout = sg_get_num(optarg);
|
||||
if (wb_timeout < 0) {
|
||||
pr2serr("Invalid argument to '--timeout'\n");
|
||||
return SG_LIB_SYNTAX_ERROR;
|
||||
}
|
||||
break;
|
||||
case 'v':
|
||||
++verbose;
|
||||
break;
|
||||
case 'V':
|
||||
pr2serr(ME "version: %s\n", version_str);
|
||||
return 0;
|
||||
default:
|
||||
pr2serr("unrecognised option code 0x%x ??\n", c);
|
||||
usage();
|
||||
return SG_LIB_SYNTAX_ERROR;
|
||||
}
|
||||
}
|
||||
if (do_help) {
|
||||
if (do_help > 1) {
|
||||
usage();
|
||||
pr2serr("\n");
|
||||
print_modes();
|
||||
} else
|
||||
usage();
|
||||
return 0;
|
||||
}
|
||||
if (optind < argc) {
|
||||
if (NULL == device_name) {
|
||||
device_name = argv[optind];
|
||||
++optind;
|
||||
}
|
||||
if (optind < argc) {
|
||||
for (; optind < argc; ++optind)
|
||||
pr2serr("Unexpected extra argument: %s\n", argv[optind]);
|
||||
usage();
|
||||
return SG_LIB_SYNTAX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (NULL == device_name) {
|
||||
pr2serr("missing device name!\n");
|
||||
usage();
|
||||
return SG_LIB_SYNTAX_ERROR;
|
||||
}
|
||||
|
||||
if ((wb_len > 0) && (bpw > wb_len)) {
|
||||
pr2serr("trim chunk size (CS) to be the same as LEN\n");
|
||||
bpw = wb_len;
|
||||
}
|
||||
|
||||
#ifdef SG_LIB_WIN32
|
||||
#ifdef SG_LIB_WIN32_DIRECT
|
||||
if (verbose > 4)
|
||||
pr2serr("Initial win32 SPT interface state: %s\n",
|
||||
scsi_pt_win32_spt_state() ? "direct" : "indirect");
|
||||
scsi_pt_win32_direct(SG_LIB_WIN32_DIRECT /* SPT pt interface */);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
sg_fd = sg_cmds_open_device(device_name, false /* rw */, verbose);
|
||||
if (sg_fd < 0) {
|
||||
pr2serr(ME "open error: %s: %s\n", device_name,
|
||||
safe_strerror(-sg_fd));
|
||||
return SG_LIB_FILE_ERROR;
|
||||
}
|
||||
if (file_name || (wb_len > 0)) {
|
||||
if (0 == wb_len)
|
||||
wb_len = DEF_XFER_LEN;
|
||||
if (NULL == (dop = (unsigned char *)malloc(wb_len))) {
|
||||
pr2serr(ME "out of memory\n");
|
||||
ret = SG_LIB_SYNTAX_ERROR;
|
||||
goto err_out;
|
||||
}
|
||||
memset(dop, 0xff, wb_len);
|
||||
if (file_name) {
|
||||
got_stdin = (0 == strcmp(file_name, "-"));
|
||||
if (got_stdin) {
|
||||
if (wb_skip > 0) {
|
||||
pr2serr("Can't skip on stdin\n");
|
||||
ret = SG_LIB_FILE_ERROR;
|
||||
goto err_out;
|
||||
}
|
||||
infd = STDIN_FILENO;
|
||||
} else {
|
||||
if ((infd = open(file_name, O_RDONLY)) < 0) {
|
||||
snprintf(ebuff, EBUFF_SZ,
|
||||
ME "could not open %s for reading", file_name);
|
||||
perror(ebuff);
|
||||
ret = SG_LIB_FILE_ERROR;
|
||||
goto err_out;
|
||||
} else if (sg_set_binary_mode(infd) < 0)
|
||||
perror("sg_set_binary_mode");
|
||||
if (wb_skip > 0) {
|
||||
if (lseek(infd, wb_skip, SEEK_SET) < 0) {
|
||||
snprintf(ebuff, EBUFF_SZ, ME "couldn't skip to "
|
||||
"required position on %s", file_name);
|
||||
perror(ebuff);
|
||||
close(infd);
|
||||
ret = SG_LIB_FILE_ERROR;
|
||||
goto err_out;
|
||||
}
|
||||
}
|
||||
}
|
||||
res = read(infd, dop, wb_len);
|
||||
if (res < 0) {
|
||||
snprintf(ebuff, EBUFF_SZ, ME "couldn't read from %s",
|
||||
file_name);
|
||||
perror(ebuff);
|
||||
if (! got_stdin)
|
||||
close(infd);
|
||||
ret = SG_LIB_FILE_ERROR;
|
||||
goto err_out;
|
||||
}
|
||||
if (res < wb_len) {
|
||||
if (wb_len_given) {
|
||||
pr2serr("tried to read %d bytes from %s, got %d bytes\n",
|
||||
wb_len, file_name, res);
|
||||
pr2serr("pad with 0xff bytes and continue\n");
|
||||
} else {
|
||||
if (verbose) {
|
||||
pr2serr("tried to read %d bytes from %s, got %d "
|
||||
"bytes\n", wb_len, file_name, res);
|
||||
pr2serr("will write %d bytes", res);
|
||||
if ((bpw > 0) && (bpw < wb_len))
|
||||
pr2serr(", %d bytes per WRITE BUFFER command\n",
|
||||
bpw);
|
||||
else
|
||||
pr2serr("\n");
|
||||
}
|
||||
wb_len = res;
|
||||
}
|
||||
}
|
||||
if (! got_stdin)
|
||||
close(infd);
|
||||
}
|
||||
}
|
||||
|
||||
res = 0;
|
||||
if (bpw > 0) {
|
||||
for (k = 0; k < wb_len; k += n) {
|
||||
n = wb_len - k;
|
||||
if (n > bpw)
|
||||
n = bpw;
|
||||
if (verbose)
|
||||
pr2serr("sending write buffer, mode=0x%x, mspec=%d, id=%d, "
|
||||
" offset=%d, len=%d\n", wb_mode, wb_mspec, wb_id,
|
||||
wb_offset + k, n);
|
||||
if (dry_run) {
|
||||
if (verbose)
|
||||
pr2serr("skipping WRITE BUFFER command due to "
|
||||
"--dry-run\n");
|
||||
res = 0;
|
||||
} else
|
||||
res = sg_ll_write_buffer_v2(sg_fd, wb_mode, wb_mspec, wb_id,
|
||||
wb_offset + k, dop + k, n,
|
||||
wb_timeout, true, verbose);
|
||||
if (res)
|
||||
break;
|
||||
}
|
||||
if (bpw_then_activate) {
|
||||
if (verbose)
|
||||
pr2serr("sending Activate deferred microcode [0xf]\n");
|
||||
if (dry_run) {
|
||||
if (verbose)
|
||||
pr2serr("skipping WRITE BUFFER(ACTIVATE) command due to "
|
||||
"--dry-run\n");
|
||||
res = 0;
|
||||
} else
|
||||
res = sg_ll_write_buffer_v2(sg_fd, MODE_ACTIVATE_MC,
|
||||
0 /* buffer_id */,
|
||||
0 /* buffer_offset */, 0,
|
||||
NULL, 0, wb_timeout, true,
|
||||
verbose);
|
||||
}
|
||||
} else {
|
||||
if (verbose)
|
||||
pr2serr("sending single write buffer, mode=0x%x, mpsec=%d, "
|
||||
"id=%d, offset=%d, len=%d\n", wb_mode, wb_mspec, wb_id,
|
||||
wb_offset, wb_len);
|
||||
if (dry_run) {
|
||||
if (verbose)
|
||||
pr2serr("skipping WRITE BUFFER(all in one) command due to "
|
||||
"--dry-run\n");
|
||||
res = 0;
|
||||
} else
|
||||
res = sg_ll_write_buffer_v2(sg_fd, wb_mode, wb_mspec, wb_id,
|
||||
wb_offset, dop, wb_len, wb_timeout,
|
||||
true, verbose);
|
||||
}
|
||||
if (0 != res) {
|
||||
char b[80];
|
||||
|
||||
ret = res;
|
||||
sg_get_category_sense_str(res, sizeof(b), b, verbose);
|
||||
pr2serr("Write buffer failed: %s\n", b);
|
||||
}
|
||||
|
||||
err_out:
|
||||
if (dop)
|
||||
free(dop);
|
||||
res = sg_cmds_close_device(sg_fd);
|
||||
if (res < 0) {
|
||||
pr2serr("close error: %s\n", safe_strerror(-res));
|
||||
if (0 == ret)
|
||||
return SG_LIB_FILE_ERROR;
|
||||
}
|
||||
return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
|
||||
}
|
@ -11,7 +11,6 @@
|
||||
*/
|
||||
|
||||
|
||||
#include "config.h"
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
|
Loading…
Reference in New Issue
Block a user