* Copyright (c) 2021-2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <cerrno>
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <fcntl.h>
#include <getopt.h>
#include <iomanip>
#include <linux/cdrom.h>
#include <openssl/sha.h>
#include <sstream>
#include <cinttypes>
#include <scsi/sg.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <unistd.h>
#include "securec.h"
#include "storage_service_errno.h"
#include "storage_service_log.h"
#include "utils/disk_utils.h"
#include "utils/file_utils.h"
#include "utils/storage_radar.h"
namespace OHOS {
namespace StorageDaemon {
using namespace std;
using namespace OHOS::StorageService;
constexpr int32_t NODE_PERM = 0660;
constexpr int32_t MIN_UUID_LENGTH = 1;
constexpr int32_t MAX_UUID_LENGTH = 40;
constexpr int32_t DEF_TIMEOUT = 120000;
constexpr int32_t SENSE_BUFF_LEN = 64;
constexpr int32_t READ_DISC_INFO_OPCODE = 0x51;
constexpr int32_t READ_DISC_INFO_SECTOR = 0x46;
constexpr uint8_t DISC_TYPE_OFFSET_HIGH = 6;
constexpr uint8_t DISC_TYPE_OFFSET_LOW = 7;
constexpr uint8_t DISC_TYPE_OFFSET = 8;
constexpr uint8_t BYTE_SHIFT_24 = 24;
constexpr uint8_t BYTE_SHIFT_16 = 16;
constexpr uint8_t BYTE_SHIFT_8 = 8;
constexpr uint8_t BYTE_MASK = 0xff;
constexpr uint8_t LAST_LBA_BYTE_0 = 0;
constexpr uint8_t LAST_LBA_BYTE_1 = 1;
constexpr uint8_t LAST_LBA_BYTE_2 = 2;
constexpr uint8_t LAST_LBA_BYTE_3 = 3;
constexpr uint8_t BLOCK_SIZE_BYTE_0 = 4;
constexpr uint8_t BLOCK_SIZE_BYTE_1 = 5;
constexpr uint8_t BLOCK_SIZE_BYTE_2 = 6;
constexpr uint8_t BLOCK_SIZE_BYTE_3 = 7;
constexpr int32_t CDB_ALLOCATION_LENGTH_HIGH = 7;
constexpr int32_t CDB_ALLOCATION_LENGTH_LOW = 8;
constexpr int32_t MAX_ALLOC_LEN = 0xFFFF;
constexpr int32_t READ_DISC_INFO_CDB_LEN = 10;
constexpr int32_t GET_CONFIG_CDB_LEN = 10;
constexpr int32_t GET_CONFIG_OPCODE = 0x46;
constexpr int32_t MODE_SENSE_CDB_LEN = 10;
constexpr int32_t MODE_SENSE_OPCODE = 0x5A;
constexpr uint8_t CAPABILITIES_PAGE_CODE = 0x2A;
constexpr uint32_t CD_SPEED_KBPS = 176;
constexpr const char *MMC_MAX_VOLUMES_PATH = "/sys/module/mmcblk/parameters/perdev_minors";
constexpr size_t INT32_SHORT_ID_LENGTH = 20;
constexpr size_t INT32_PLAINTEXT_LENGTH = 4;
constexpr size_t INT32_MIN_ID_LENGTH = 3;
constexpr size_t SHA256_DIGEST_BIT_MASK = 0x0f;
constexpr size_t SHA256_DIGEST_VERSION = 0x50;
constexpr size_t SHA256_VARIANT_MASK = 0x3f;
constexpr size_t SHA256_IETF_VARIANT = 0x80;
constexpr uint8_t UUID_NAMESPACE_RAW_SIZE = 32;
constexpr uint8_t UUID_DIGEST_BYTE_OFFSET = 6;
constexpr uint8_t UUID_VARIANT_BYTE_OFFSET = 8;
constexpr uint8_t UUID_TIME_LO_FIELD_WIDTH = 8;
constexpr uint8_t UUID_TIME_MID_FIELD_WIDTH = 4;
constexpr uint8_t UUID_TIME_HI_VERSION_FIELD_WIDTH = 4;
constexpr uint8_t UUID_CLOCK_SEQ_FIELD_WIDTH = 4;
constexpr uint8_t UUID_NODE_ID_FIELD_WIDTH = 12;
constexpr uint8_t UUID_DIGEST_TIME_MID_OFFSET = 4;
constexpr uint8_t UUID_DIGEST_TIME_HI_VERSION_OFFSET = 6;
constexpr uint8_t UUID_DIGEST_CLOCK_SEQ_OFFSET = 8;
constexpr uint8_t UUID_DIGEST_NODE_ID_OFFSET = 10;
constexpr uint8_t GET_CAPACITY_CMD_BUF_LEN = 16;
constexpr uint8_t GET_CAPACITY_DATA_BUF_LEN = 48;
constexpr uint8_t GET_CD_USED_CAPACITY_CMD_LEN = 10;
constexpr uint8_t GET_CD_USED_CAPACITY_DATA_LEN = 28;
constexpr uint8_t GET_CD_TOTAL_CAPACITY_CMD_LEN = 10;
constexpr uint8_t GET_CD_TOTAL_CAPACITY_DATA_LEN = 4;
constexpr uint8_t GET_DVD_USED_CAPACITY_CMD_LEN = 10;
constexpr uint8_t GET_DVD_USED_CAPACITY_DATA_LEN = 8;
constexpr uint8_t GET_DVD_TOTAL_CAPACITY_CMD_LEN = 12;
constexpr uint8_t GET_DVD_TOTAL_CAPACITY_DATA_LEN = 36;
constexpr uint16_t ODD_LOGICAL_SECTOR_SIZE = 2048;
constexpr uint8_t CD_SECTORS_PER_SECOND = 75;
constexpr uint8_t SECONDS_PER_MINUTES = 60;
int CreateDiskNode(const std::string &path, dev_t dev)
{
LOGD("[L8:DiskUtils] CreateDiskNode: >>> ENTER <<< path=%{public}s", path.c_str());
if (mknod(path.c_str(), NODE_PERM | S_IFBLK, dev) < 0) {
LOGE("[L8:DiskUtils] CreateDiskNode: <<< EXIT FAILED <<< create disk node failed, errno=%{public}d", errno);
return E_ERR;
}
LOGD("[L8:DiskUtils] CreateDiskNode: <<< EXIT SUCCESS <<<");
return E_OK;
}
int DestroyDiskNode(const std::string &path)
{
LOGD("[L8:DiskUtils] DestroyDiskNode: >>> ENTER <<< path=%{public}s", path.c_str());
if (TEMP_FAILURE_RETRY(unlink(path.c_str())) < 0) {
LOGE("[L8:DiskUtils] DestroyDiskNode: <<< EXIT FAILED <<< unlink failed, errno=%{public}d", errno);
return E_ERR;
}
LOGD("[L8:DiskUtils] DestroyDiskNode: <<< EXIT SUCCESS <<<");
return E_OK;
}
int GetDevSize(const std::string &path, uint64_t *size)
{
LOGD("[L8:DiskUtils] GetDevSize: >>> ENTER <<< path=%{private}s", path.c_str());
FILE *f = fopen(path.c_str(), "r");
if (f == nullptr) {
LOGE("[L8:DiskUtils] GetDevSize: <<< EXIT FAILED <<< open failed, errno=%{public}d", errno);
return E_ERR;
}
int fd = fileno(f);
if (fd < 0) {
LOGE("[L8:DiskUtils] GetDevSize: <<< EXIT FAILED <<< open failed, errno=%{public}d", errno);
(void)fclose(f);
return E_ERR;
}
if (ioctl(fd, BLKGETSIZE64, size)) {
LOGE("[L8:DiskUtils] GetDevSize: <<< EXIT FAILED <<< get device size failed, errno=%{public}d", errno);
(void)fclose(f);
return E_ERR;
}
(void)fclose(f);
LOGD("[L8:DiskUtils] GetDevSize: <<< EXIT SUCCESS <<< size=%{public}" PRIu64, *size);
return E_OK;
}
int GetMaxVolume(dev_t device)
{
LOGD("[L8:DiskUtils] GetMaxVolume: >>> ENTER <<<");
unsigned int majorId = major(device);
if (majorId == DISK_MMC_MAJOR) {
std::string str;
if (!ReadFile(MMC_MAX_VOLUMES_PATH, &str)) {
LOGE("[L8:DiskUtils] GetMaxVolume: <<< EXIT FAILED <<< Get MmcMaxVolumes failed");
return E_ERR;
}
return std::atoi(str.c_str());
} else {
LOGD("[L8:DiskUtils] GetMaxVolume: <<< EXIT SUCCESS <<< MAX_SCSI_VOLUMES");
return MAX_SCSI_VOLUMES;
}
}
bool IsAcceptableUuid(const std::string &uuid)
{
if (uuid.empty()) {
return false;
}
static const std::string forbidden = "/\\";
if (uuid.find_first_of(forbidden) != std::string::npos) {
return false;
}
if (uuid.length() < MIN_UUID_LENGTH || uuid.length() > MAX_UUID_LENGTH) {
return false;
}
if (uuid == "." || uuid == "..") {
return false;
}
return true;
}
int32_t ReadMetadata(const std::string &devPath, std::string &uuid, std::string &type, std::string &label)
{
LOGI("[L8:DiskUtils] ReadMetadata: >>> ENTER <<< devPath=%{public}s", devPath.c_str());
uuid = GetBlkidData(devPath, "UUID");
type = GetBlkidData(devPath, "TYPE");
label = GetBlkidData(devPath, "LABEL");
LOGI("[L8:DiskUtils] ReadMetadata: fsUuid=%{public}s, fsType=%{public}s, fsLabel=%{public}s",
GetAnonyString(uuid).c_str(), type.c_str(), label.c_str());
if (type.empty() || !IsAcceptableUuid(uuid)) {
LOGE("[L8:DiskUtils] ReadMetadata: <<< EXIT FAILED <<< External volume ReadMetadata error");
return E_READMETADATA;
}
LOGI("[L8:DiskUtils] ReadMetadata: <<< EXIT SUCCESS <<<");
return E_OK;
}
int32_t ReadVolumeUuid(const std::string &devPath, std::string &uuid)
{
LOGI("[L8:DiskUtils] ReadVolumeUuid: >>> ENTER <<< devPath=%{public}s", devPath.c_str());
uuid = GetBlkidData(devPath, "UUID");
LOGI("[L8:DiskUtils] ReadVolumeUuid: fsUuid=%{public}s", GetAnonyString(uuid).c_str());
if (uuid.empty()) {
LOGE("[L8:DiskUtils] ReadVolumeUuid: <<< EXIT FAILED <<< External volume ReadMetadata error");
return E_READMETADATA;
}
LOGI("[L8:DiskUtils] ReadVolumeUuid: <<< EXIT SUCCESS <<<");
return E_OK;
}
std::string GetBlkidData(const std::string &devPath, const std::string &type)
{
LOGD("[L8:DiskUtils] GetBlkidData: >>> ENTER <<< devPath=%{public}s, type=%{public}s",
devPath.c_str(), type.c_str());
std::vector<std::string> cmd;
cmd = {
"blkid",
"-n",
"mdraid",
"-s",
type,
"-o",
"value",
devPath
};
return GetBlkidDataByCmd(cmd);
}
std::string GetBlkidDataByCmd(std::vector<std::string> &cmd)
{
std::vector<std::string> output;
int32_t err = ForkExec(cmd, &output);
for (auto str : output) {
LOGI("GetBlkidDataByCmd output: %{public}s", str.c_str());
}
if (err) {
StorageRadar::ReportVolumeOperation("ForkExec", err);
LOGE("[L8:DiskUtils] GetBlkidDataByCmd: <<< EXIT FAILED <<< ForkExec failed, err=%{public}d", err);
return "";
}
if (output.size() > 0) {
size_t sep = output[0].find_first_of("\n");
if (sep != string::npos)
output[0].resize(sep);
LOGD("[L8:DiskUtils] GetBlkidDataByCmd: <<< EXIT SUCCESS <<<");
return output[0];
}
LOGD("[L8:DiskUtils] GetBlkidDataByCmd: <<< EXIT SUCCESS <<< empty result");
return "";
}
std::string GenerateRandomUuid(const std::string &diskPath, const std::string &uuidFormat)
{
LOGD("[L8:DiskUtils] GenerateRandomUuid: >>> ENTER <<< diskPath=%{public}s", diskPath.c_str());
unsigned char hash[SHA256_DIGEST_LENGTH];
SHA256_CTX ctxSeed;
SHA256_Init(&ctxSeed);
SHA256_Update(&ctxSeed, uuidFormat.c_str(), uuidFormat.length());
SHA256_Final(hash, &ctxSeed);
unsigned char namespaceRaw[UUID_NAMESPACE_RAW_SIZE];
std::copy(hash, hash + UUID_NAMESPACE_RAW_SIZE, namespaceRaw);
unsigned char digest[SHA256_DIGEST_LENGTH];
SHA256_CTX ctx;
SHA256_Init(&ctx);
SHA256_Update(&ctx, namespaceRaw, sizeof(namespaceRaw));
SHA256_Update(&ctx, diskPath.c_str(), diskPath.length());
SHA256_Final(digest, &ctx);
digest[UUID_DIGEST_BYTE_OFFSET] &= SHA256_DIGEST_BIT_MASK;
digest[UUID_DIGEST_BYTE_OFFSET] |= SHA256_DIGEST_VERSION;
digest[UUID_VARIANT_BYTE_OFFSET] &= SHA256_VARIANT_MASK;
digest[UUID_VARIANT_BYTE_OFFSET] |= SHA256_IETF_VARIANT;
std::ostringstream uuidStream;
uuidStream << std::hex << std::setfill('0') << std::uppercase
<< std::setw(UUID_TIME_LO_FIELD_WIDTH) << std::hex << *reinterpret_cast<uint32_t*>(digest) << '-'
<< std::setw(UUID_TIME_MID_FIELD_WIDTH) << *reinterpret_cast<uint16_t*>(digest +
UUID_DIGEST_TIME_MID_OFFSET) << '-'
<< std::setw(UUID_TIME_HI_VERSION_FIELD_WIDTH) << *reinterpret_cast<uint16_t*>(digest +
UUID_DIGEST_TIME_HI_VERSION_OFFSET) << '-'
<< std::setw(UUID_CLOCK_SEQ_FIELD_WIDTH) << *reinterpret_cast<uint16_t*>(digest +
UUID_DIGEST_CLOCK_SEQ_OFFSET) << '-'
<< std::setw(UUID_NODE_ID_FIELD_WIDTH) << *reinterpret_cast<uint64_t*>(digest +
UUID_DIGEST_NODE_ID_OFFSET);
LOGD("[L8:DiskUtils] GenerateRandomUuid: <<< EXIT SUCCESS <<<");
return uuidStream.str();
}
std::string GetAnonyString(const std::string &value)
{
std::string res;
std::string tmpStr("******");
size_t strLen = value.length();
if (strLen < INT32_MIN_ID_LENGTH) {
return tmpStr;
}
if (strLen <= INT32_SHORT_ID_LENGTH) {
res += value[0];
res += tmpStr;
res += value[strLen - 1];
} else {
res.append(value, 0, INT32_PLAINTEXT_LENGTH);
res += tmpStr;
res.append(value, strLen - INT32_PLAINTEXT_LENGTH, INT32_PLAINTEXT_LENGTH);
}
return res;
}
int SendScsiCmd(int fd, uint8_t *cdb, int cdbLen, uint8_t *dxferp, int dxferLen)
{
LOGD("[L8:DiskUtils] SendScsiCmd: >>> ENTER <<< fd=%{public}d", fd);
sg_io_hdr_t ioHdr;
uint8_t sense[SENSE_BUFF_LEN];
if (memset_s(&ioHdr, sizeof(ioHdr), 0, sizeof(ioHdr)) != 0) {
LOGE("[L8:DiskUtils] SendScsiCmd: <<< EXIT FAILED <<< ioHdr memset_s failed");
return E_ERR;
}
if (memset_s(sense, sizeof(sense), 0, sizeof(sense)) != 0) {
LOGE("[L8:DiskUtils] SendScsiCmd: <<< EXIT FAILED <<< sense memset_s failed");
return E_ERR;
}
ioHdr.interface_id = 'S';
ioHdr.dxfer_direction = SG_DXFER_FROM_DEV;
ioHdr.cmdp = cdb;
ioHdr.cmd_len = cdbLen;
ioHdr.dxferp = dxferp;
ioHdr.dxfer_len = static_cast<unsigned int>(dxferLen);
ioHdr.mx_sb_len = sizeof(sense);
ioHdr.sbp = sense;
ioHdr.timeout = DEF_TIMEOUT;
if (ioctl(fd, SG_IO, &ioHdr) < 0) {
LOGE("[L8:DiskUtils] SendScsiCmd: <<< EXIT FAILED <<< ioctl SG_IO failed");
return E_ERR;
}
if ((ioHdr.info & SG_INFO_OK_MASK) != SG_INFO_OK) {
std::string senseStr;
for (int i = 0; i < ioHdr.mx_sb_len && i < SENSE_BUFF_LEN; i++) {
senseStr += (i > 0 ? "," : "") + std::to_string(sense[i]);
}
LOGE("[L8:DiskUtils] SendScsiCmd: <<< EXIT FAILED <<< SG_INFO not OK, sense=[%{public}s]", senseStr.c_str());
return E_ERR;
}
LOGD("[L8:DiskUtils] SendScsiCmd: <<< EXIT SUCCESS <<<");
return E_OK;
}
int GetCDStatus(const char *device, int &status)
{
char realPath[PATH_MAX] = { 0 };
if (realpath(device, realPath) == nullptr) {
LOGE("realpath failed.");
return E_FILE_PATH_INVALID;
}
FILE* file = fopen(realPath, "rb");
if (file == nullptr) {
LOGE("fopen failed errStr:%{public}s errno:%{public}d", strerror(errno), errno);
return E_SYS_KERNEL_ERR;
}
int fd = fileno(file);
if (fd < 0) {
LOGE("fileno failed, errStr:%{public}s errno:%{public}d", strerror(errno), errno);
(void)fclose(file);
return E_SYS_KERNEL_ERR;
}
* CDS_NO_DISC - 1 - No disk.
* CDS_TRAY_OPEN - 2 - Tray open.
* CDS_DRIVE_NOT_READY - 3 - CD-Rom in tray but not ready, wait init.
* CDS_DISC_OK - 4 - CD-Rom is ready.
*/
auto startTime = StorageService::StorageRadar::RecordCurrentTime();
int slot = INT_MAX;
status = ioctl(fd, CDROM_DRIVE_STATUS, &slot);
if (status < 0) {
LOGE("CD status:%{public}d errStr:%{public}s errno:%{public}d", status, strerror(errno), errno);
(void)fclose(file);
return E_ERR;
}
(void)fclose(file);
auto delay = StorageService::StorageRadar::ReportDuration("GET CD STATUS: CD ROM",
startTime, StorageService::DELAY_TIME_THRESH_HIGH, 0);
LOGW("SD_DURATION: GET CD STATUS: device=%{public}s, delay = %{public}s", device, delay.c_str());
return E_OK;
}
int SendScsiCmdByPath(const std::string &diskPath, uint8_t *cdb, int cdbLen, uint8_t *buf, int len)
{
LOGI("[L8:DiskUtils] SendScsiCmdByPath: >>> ENTER <<< diskPath=%{public}s, cdbLen=%{public}d, len=%{public}d",
diskPath.c_str(), cdbLen, len);
std::string cdbStr;
for (int i = 0; i < cdbLen; i++) {
cdbStr += (i > 0 ? "," : "") + std::to_string(cdb[i]);
}
LOGI("[L8:DiskUtils] SendScsiCmdByPath: cdb=%{public}s", cdbStr.c_str());
char realPath[PATH_MAX] = { 0 };
if (realpath(diskPath.c_str(), realPath) == nullptr) {
LOGE("[L8:DiskUtils] SendScsiCmdByPath: <<< EXIT FAILED <<< realpath failed");
return E_ERR;
}
FILE* file = fopen(realPath, "rb");
if (file == nullptr) {
return E_ERR;
}
int fd = fileno(file);
if (fd < 0) {
(void)fclose(file);
return E_ERR;
}
int ret = SendScsiCmd(fd, cdb, cdbLen, buf, len);
(void)fclose(file);
LOGI("[L8:DiskUtils] SendScsiCmdByPath: <<< EXIT SUCCESS <<< ret=%{public}d", ret);
return ret;
}
int ReadDiscInfo(const std::string &diskPath, int32_t cmdIndex, uint8_t *buf, int len)
{
LOGD("[L8:DiskUtils] ReadDiscInfo: >>> ENTER <<< diskPath=%{public}s, len=%{public}d", diskPath.c_str(), len);
uint8_t cdb[READ_DISC_INFO_CDB_LEN] = { static_cast<uint8_t>(cmdIndex) };
cdb[CDB_ALLOCATION_LENGTH_HIGH] = static_cast<uint8_t>(static_cast<uint32_t>(len) >> CDB_ALLOCATION_LENGTH_LOW);
cdb[CDB_ALLOCATION_LENGTH_LOW] = static_cast<uint8_t>(static_cast<uint32_t>(len) & MAX_ALLOC_LEN);
int ret = SendScsiCmdByPath(diskPath, cdb, sizeof(cdb), buf, len);
if (ret != 0) {
LOGE("[L8:DiskUtils] ReadDiscInfo: <<< EXIT FAILED <<< SendScsiCmdByPath failed, err=%{public}d", ret);
return ret;
}
LOGD("[L8:DiskUtils] ReadDiscInfo: <<< EXIT SUCCESS <<<");
return E_OK;
}
int ReadConfiguration(const std::string &diskPath, uint8_t *buf, int len)
{
LOGI("[L8:DiskUtils] ReadConfiguration: >>> ENTER <<< diskPath=%{public}s, len=%{public}d", diskPath.c_str(), len);
if (len <= 0 || buf == nullptr) {
LOGE("[L8:DiskUtils] ReadConfiguration: invalid params, len=%{public}d", len);
return E_PARAMS_INVALID;
}
uint32_t allocLen = static_cast<uint32_t>(len);
uint8_t cdb[GET_CONFIG_CDB_LEN] = { GET_CONFIG_OPCODE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
static_cast<uint8_t>((allocLen >> 8) & 0xFF), static_cast<uint8_t>(allocLen & 0xFF) };
int ret = SendScsiCmdByPath(diskPath, cdb, sizeof(cdb), buf, len);
if (ret == 0) {
std::string respStr;
for (int i = 0; i < std::min(len, SENSE_BUFF_LEN); i++) {
respStr += (i > 0 ? "," : "") + std::to_string(buf[i]);
}
LOGI("[L8:DiskUtils] ReadConfiguration: RESP(first 64 bytes)=[%{public}s]", respStr.c_str());
}
LOGI("[L8:DiskUtils] ReadConfiguration: <<< EXIT SUCCESS <<< ret=%{public}d", ret);
return ret;
}
int IsExistCD(const std::string &diskPath, bool &isExistCD)
{
LOGD("[L8:DiskUtils] IsExistCD: >>> ENTER <<< diskPath=%{public}s", diskPath.c_str());
int status = 0;
isExistCD = false;
int ret = GetCDStatus(diskPath.c_str(), status);
if (ret != E_OK) {
LOGE("Unknown cd status !");
return E_ERR;
}
isExistCD = (status == CDS_DRIVE_NOT_READY || status == CDS_DISC_OK);
LOGI("[L8:DiskUtils] IsExistCD:Current CD statue, isExistCD: %{public}d", isExistCD);
return E_OK;
}
int IsBlankCD(const std::string &diskPath, bool &isBlankCD)
{
isBlankCD = false;
bool isExistCD = false;
int existRet = IsExistCD(diskPath, isExistCD);
if (existRet != E_OK || !isExistCD) {
LOGE("CD is not exist, please check ! existRet:%{public}d, isExistCD:%{public}d", existRet, isExistCD);
return E_ERR;
}
uint8_t buf[MAX_BUF];
if (ReadDiscInfo(diskPath, READ_DISC_INFO_OPCODE, buf, sizeof(buf)) == E_OK) {
uint8_t discStatus = buf[DISC_STATUS_BYTE_INDEX] & DISC_STATUS_MASK;
isBlankCD = (discStatus == 0);
LOGI("[L8:DiskUtils] IsBlankCD: <<< EXIT SUCCESS <<< isBlank=%{public}d", isBlankCD);
return E_OK;
} else {
LOGE("[L8:DiskUtils] IsBlankCD: <<< EXIT FAILED <<< Unable to read disc information");
}
LOGE("Unable to read disc information.");
return E_ERR;
}
std::string DiskType2Str(uint8_t diskType)
{
LOGD("[L8:DiskUtils] DiskType2Str: >>> ENTER <<< diskType=0x%{public}x", diskType);
switch (diskType) {
case 0x08:
return "CD-ROM";
case 0x09:
return "CD-R";
case 0x0A:
return "CD-RW";
case 0x10:
return "DVD-ROM";
case 0x11:
return "DVD-R";
case 0x12:
return "DVD-RAM";
case 0x13:
case 0x14:
return "DVD-RW";
case 0x1A:
return "DVD+RW";
case 0x1B:
case 0x1C:
return "DVD+R";
case 0x1D:
return "DVD+RW";
case 0x40:
return "BD-ROM";
case 0x41:
return "BD-R";
case 0x42:
case 0x43:
return "BD-RE";
default:
LOGW("[L8:DiskUtils] DiskType2Str: unknown diskType=0x%{public}x", diskType);
return "";
}
}
std::string GetCDType(const std::string &diskPath)
{
LOGD("[L8:DiskUtils] GetCDType: >>> ENTER <<< diskPath=%{public}s", diskPath.c_str());
uint8_t buf[MAX_BUF];
if (ReadDiscInfo(diskPath, READ_DISC_INFO_SECTOR, buf, sizeof(buf)) == 0) {
return DiskType2Str((buf[DISC_TYPE_OFFSET_HIGH] << DISC_TYPE_OFFSET) | buf[DISC_TYPE_OFFSET_LOW]);
} else {
LOGE("[L8:DiskUtils] GetCDType: <<< EXIT FAILED <<< Unable to read disc information");
}
return "";
}
std::string GetOpticalDriveType(const std::string &diskPath)
{
LOGI("[L8:DiskUtils] GetOpticalDriveType: >>> ENTER <<< diskPath=%{public}s", diskPath.c_str());
uint8_t buf[MAX_BUF];
const size_t FEATURE_START = 8;
const size_t LOW_BYTE_OFFSET = 1;
const size_t ADDITIONAL_LEN = 3;
const int FEATURE_CODE_LENGTH = 4;
if (ReadConfiguration(diskPath, buf, sizeof(buf)) != E_OK) {
LOGE("[L8:DiskUtils] GetOpticalDriveType: <<< EXIT FAILED <<< GetConfiguration not supported");
return "";
}
uint32_t dataLen = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
if (dataLen < FEATURE_CODE_LENGTH || dataLen > MAX_BUF - FEATURE_CODE_LENGTH) {
LOGE("[L8:DiskUtils] GetOpticalDriveType: <<< EXIT FAILED <<< Invalid data length=%{public}d",
dataLen);
return "";
}
uint16_t currentProfile = (buf[6] << 8) | buf[7];
LOGI("[L8:DiskUtils] GetOpticalDriveType: currentProfile=0x%{public}x", currentProfile);
bool hasDvdRw = false;
bool hasDvdR = false;
bool hasBdRe = false;
bool hasBdR = false;
bool hasBdRom = false;
for (size_t i = FEATURE_START;
i + ADDITIONAL_LEN < std::min(static_cast<size_t>(dataLen + FEATURE_CODE_LENGTH), sizeof(buf));) {
uint16_t feature = (buf[i] << FEATURE_START) | buf[i + LOW_BYTE_OFFSET];
uint8_t additionalLen = buf[i + ADDITIONAL_LEN];
if (feature == 0x002D) {
hasDvdRw = true;
} else if (feature == 0x002E) {
hasDvdR = true;
} else if (feature == 0x0042) {
hasBdRe = true;
} else if (feature == 0x0041) {
hasBdR = true;
} else if (feature == 0x0040) {
hasBdRom = true;
}
i += FEATURE_CODE_LENGTH + additionalLen;
}
if (hasBdRe || hasBdR || hasBdRom) {return "BD-RE";}
if (hasDvdRw || hasDvdR) {return "DVD RW";}
if (currentProfile != 0) {
return DiskType2Str(static_cast<uint8_t>(currentProfile & 0xFF));
}
LOGW("[L8:DiskUtils] GetOpticalDriveType: <<< EXIT NO MATCH <<< No matching profile found");
return "";
}
int GetOpticalDriveMaxWriteSpeed(const std::string &diskPath, int32_t &maxWriteSpeed)
{
LOGI("[L8:DiskUtils] GetOpticalDriveMaxWriteSpeed: >>> ENTER <<< diskPath=%{public}s", diskPath.c_str());
uint8_t cdb[MODE_SENSE_CDB_LEN] = { MODE_SENSE_OPCODE,
0x00, CAPABILITIES_PAGE_CODE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00 };
std::string cdbStr;
for (size_t i = 0; i < sizeof(cdb); i++) {
cdbStr += (i > 0 ? "," : "") + std::to_string(cdb[i]);
}
LOGI("[L8:DiskUtils] GetOpticalDriveMaxWriteSpeed: cdb=%{public}s", cdbStr.c_str());
uint8_t buf[128] = {0};
int ret = SendScsiCmdByPath(diskPath, cdb, sizeof(cdb), buf, sizeof(buf));
if (ret != E_OK) {
LOGE("[L8:DiskUtils] GetOpticalDriveMaxWriteSpeed: <<< EXIT FAILED <<< ModeSense failed, ret=%{public}d", ret);
return ret;
}
std::string respStr;
for (int i = 0; i < SENSE_BUFF_LEN; i++) {
respStr += (i > 0 ? "," : "") + std::to_string(buf[i]);
}
LOGI("[L8:DiskUtils] GetOpticalDriveMaxWriteSpeed: ModeSense response=%{public}s",
respStr.c_str());
uint16_t maxWriteSpeedKBps = (buf[26] << 8) | buf[27];
LOGI("[L8:DiskUtils] GetOpticalDriveMaxWriteSpeed: MaxWriteSpeedKBps=%{public}u",
maxWriteSpeedKBps);
if (maxWriteSpeedKBps > 0) {
std::string driveType = GetOpticalDriveType(diskPath);
LOGI("[L8:DiskUtils] GetOpticalDriveMaxWriteSpeed: driveType=%{public}s",
driveType.c_str());
constexpr uint32_t DVD_SPEED_KBPS = 1385;
constexpr uint32_t BD_SPEED_KBPS = 4500;
uint32_t speedBase = CD_SPEED_KBPS;
if (driveType.find("BD") != std::string::npos || driveType.find("bd") != std::string::npos) {
speedBase = BD_SPEED_KBPS;
} else if (driveType.find("DVD") != std::string::npos || driveType.find("dvd") != std::string::npos) {
speedBase = DVD_SPEED_KBPS;
}
maxWriteSpeed = static_cast<int32_t>(maxWriteSpeedKBps / speedBase);
LOGI("[L8:DiskUtils] GetOpticalDriveMaxWriteSpeed: <<< EXIT SUCCESS <<< maxWriteSpeed=%{public}d",
maxWriteSpeed);
return E_OK;
}
LOGE("[L8:DiskUtils] GetOpticalDriveMaxWriteSpeed: <<< EXIT NO MATCH <<< No speed data found");
return E_ERR;
}
int Eject(const std::string &devPath)
{
LOGI("[L8:DiskUtils] Eject: >>> ENTER <<< devPath=%{public}s", devPath.c_str());
std::vector<std::string> output;
std::vector<std::string> cmd = {
"eject",
"-s",
devPath
};
int res = ForkExec(cmd, &output);
for (auto str : output) {
LOGI("Eject output: %{public}s", str.c_str());
}
if (res != E_OK) {
LOGE("[L8:DiskUtils] Eject: <<< EXIT FAILED <<< ForkExec eject failed, err=%{public}d", res);
return res;
}
LOGI("[L8:DiskUtils] Eject: <<< EXIT SUCCESS <<<");
return E_OK;
}
int GetCdTotalCapacity(int fd, int64_t &cdTotalCapacity)
{
unsigned char cmd_buf[GET_CAPACITY_CMD_BUF_LEN] = {0};
int cmd_len = GET_CD_TOTAL_CAPACITY_CMD_LEN;
unsigned char data_buf[GET_CAPACITY_DATA_BUF_LEN] = {0};
unsigned int data_len = GET_CD_TOTAL_CAPACITY_DATA_LEN;
unsigned int actual_len = 0;
int ret = 0;
double total_seconds = 0;
* 功能:通过 SCSI READ TOC 指令获取cd光盘的 ATIP 信息。
* ATIP 包含光盘介质类型、制造商及容量等底层参数。
* 参数说明:
* cmd_buf[2] = 0x04: 指定读取格式为 ATIP。
* cmd_buf[7-8]: 定义设备返回数据的最大长度。
*/
cmd_buf[0] = GPCMD_READ_TOC_PMA_ATIP;
cmd_buf[2] = 0x04;
cmd_buf[7] = (data_len >> 8) & 0xff;
cmd_buf[8] = data_len & 0xff;
ret = SendScsiCmd(fd, cmd_buf, cmd_len, data_buf, data_len);
if (ret != 0) {
LOGE("get atip data len failed, ret val is %{public}d", ret);
return E_ERR;
}
actual_len = (int)(data_buf[0] << 8) | data_buf[1];
actual_len += 2;
LOGI("actual_len: %{public}d", actual_len);
cmd_buf[7] = (actual_len >> 8) & 0xff;
cmd_buf[8] = actual_len & 0xff;
ret = SendScsiCmd(fd, cmd_buf, cmd_len, data_buf, actual_len);
if (ret != 0) {
LOGE("get atip data failed, ret is %{public}d", ret);
return E_ERR;
}
total_seconds = (double)data_buf[12] * SECONDS_PER_MINUTES + data_buf[13] +
(double)data_buf[14] / CD_SECTORS_PER_SECOND;
cdTotalCapacity = static_cast<int64_t>(total_seconds * CD_SECTORS_PER_SECOND * ODD_LOGICAL_SECTOR_SIZE);
LOGI("cd total_seconds: %{public}f, total_capacity: %{public}" PRIu64, total_seconds, cdTotalCapacity);
return E_OK;
}
int GetCdUsedCapacity(int fd, int64_t &cdUsedCapacity)
{
unsigned char cmd_buf[GET_CAPACITY_CMD_BUF_LEN] = {0};
int cmd_len = GET_CD_USED_CAPACITY_CMD_LEN;
unsigned char data_buf[GET_CAPACITY_DATA_BUF_LEN] = {0};
unsigned int data_len = GET_CD_USED_CAPACITY_DATA_LEN;
int ret = 0;
* 使用 SCSI READ TRACK INFORMATION 指令 (0x52) 获取cd光盘轨道/逻辑分区信息
* cmd_buf[0]: 指令操作码 0x52 (READ TRACK/RZONE INFORMATION)
* cmd_buf[1]: 设置为 1,表示通过轨道编号 (Track Number) 寻址
* cmd_buf[5]: 设置为 0xff,通常用于请求最后一条轨道 (Invisible Track) 或特定状态信息
* cmd_buf[7-8]: 分别填充 data_len 的高8位和低8位,告知驱动器返回数据的长度限制
*/
cmd_buf[0] = GPCMD_READ_TRACK_RZONE_INFO;
cmd_buf[1] = 1;
cmd_buf[5] = 0xff;
cmd_buf[7] = (data_len >> 8) & 0xff;
cmd_buf[8] = data_len & 0xff;
ret = SendScsiCmd(fd, cmd_buf, cmd_len, data_buf, data_len);
if (ret != 0) {
LOGE("get cd used capacity failed, ret val is %{public}d", ret);
return E_ERR;
}
cdUsedCapacity = ((unsigned long long)data_buf[8] << 24) |
((unsigned long long)data_buf[9] << 16) |
((unsigned long long)data_buf[10] << 8) |
data_buf[11];
cdUsedCapacity *= ODD_LOGICAL_SECTOR_SIZE;
LOGI("cd used_capacity: %{public}" PRIu64, cdUsedCapacity);
return E_OK;
}
int GetDvdTotalCapacity(int fd, int64_t &dvdTotalCapacity)
{
unsigned char cmd_buf[GET_CAPACITY_CMD_BUF_LEN] = {0};
int cmd_len = GET_DVD_TOTAL_CAPACITY_CMD_LEN;
unsigned char data_buf[GET_CAPACITY_DATA_BUF_LEN] = {0};
int data_len = GET_DVD_TOTAL_CAPACITY_DATA_LEN;
int ret = 0;
unsigned long long phys_start = 0;
unsigned long long phys_end = 0;
int dvd_media = 0;
ret = GetDvdConfiguration(fd, dvd_media);
if (ret != 0) {
LOGE("get dvd configuration failed,ret val is %{public}d", ret);
return E_ERR;
}
* 使用 SCSI READ DVD STRUCTURE 指令 (0xAD) 获取光盘物理结构或容量信息
* cmd_buf[0]: 操作码 0xAD (GPCMD_READ_DVD_STRUCTURE)。
* cmd_buf[7]: 格式码 (Format Code)。
* 如果 dvd_media <= 0x18 (通常指 DVD-ROM/R/RW),设置为 16 (0x10),
* 这对应 "DVD Structure List",用于列出支持的结构。
* 否则设置为 0,通常对应 "Physical Format Information",读取最基础的物理层信息。
* cmd_buf[9]: 分配长度 (Allocation Length) 的低位。
* 这里通常设置为数据结构的总长度 (GET_DVD_TOTAL_CAPACITY_DATA_LEN)。
* cmd_buf[11]:保留位/控制字节,通常设为 0。
*/
cmd_buf[0] = GPCMD_READ_DVD_STRUCTURE;
cmd_buf[7] = dvd_media <= 0x18 ? 16 : 0;
cmd_buf[9] = GET_DVD_TOTAL_CAPACITY_DATA_LEN;
cmd_buf[11] = 0;
ret = SendScsiCmd(fd, cmd_buf, cmd_len, data_buf, data_len);
if (ret != 0) {
LOGE("get dvd total capacity failed, ret val is %{public}d", ret);
return E_ERR;
}
phys_start = ((unsigned long long)(data_buf[9]) << 16) |
((unsigned long long)(data_buf[10]) << 8) |
data_buf[11];
if ((data_buf[6] & 0x60) == 0) {
phys_end = ((unsigned long long)(data_buf[13]) << 16) |
((unsigned long long)(data_buf[14]) << 8) |
data_buf[15];
} else {
phys_end = ((unsigned long long)(data_buf[17]) << 16) |
((unsigned long long)(data_buf[18]) << 8) |
data_buf[19];
}
if (phys_end > phys_start) {
dvdTotalCapacity = static_cast<int64_t>(phys_end - phys_start + 1) * ODD_LOGICAL_SECTOR_SIZE;
} else {
dvdTotalCapacity = 0;
LOGI("phys_end is less than phys_start, error data");
}
LOGI("dvd dvdTotalCapacity: %{public}" PRIu64, dvdTotalCapacity);
return E_OK;
}
int GetDvdUsedCapacity(int fd, int64_t &dvdUsedCapcity)
{
unsigned char cmd_buf[GET_CAPACITY_CMD_BUF_LEN] = {0};
int cmd_len = GET_DVD_USED_CAPACITY_CMD_LEN;
unsigned char data_buf[GET_CAPACITY_DATA_BUF_LEN] = {0};
unsigned int data_len = GET_DVD_USED_CAPACITY_DATA_LEN;
int ret = 0;
unsigned int blk_cnt = 0;
unsigned int blk_size = 0;
* 使用 SCSI READ CAPACITY 指令 (0x25) 获取光盘介质的逻辑容量。
* 字段详解:
* cmd_buf[0]: 操作码 0x25 (GPCMD_READ_CDVD_CAPACITY)。
* 用于请求光盘的最后逻辑块地址 (Last LBA) 和块大小 (Block Length)。
* cmd_buf[7-8]: 分配长度 (Allocation Length)。
* 告知驱动器返回数据的最大长度(通常为 8 字节)。
* 高 8 位填入 cmd_buf[7],低 8 位填入 cmd_buf[8]。
* 注意:此指令返回的 LBA 需加 1 才是总扇区数。
*/
cmd_buf[0] = GPCMD_READ_CDVD_CAPACITY;
cmd_buf[7] = (data_len >> 8) & 0xff;
cmd_buf[8] = data_len & 0xff;
ret = SendScsiCmd(fd, cmd_buf, cmd_len, data_buf, data_len);
if (ret != 0) {
LOGE("get dvd total capacity failed, ret val is %{public}d", ret);
return E_ERR;
}
blk_cnt = ((unsigned int)data_buf[0] << 24) |
((unsigned int)data_buf[1] << 16) |
((unsigned int)data_buf[2] << 8) |
data_buf[3];
blk_size = ((unsigned int)data_buf[4] << 24) |
((unsigned int)data_buf[5] << 16) |
((unsigned int)data_buf[6] << 8) |
data_buf[7];
dvdUsedCapcity = static_cast<int64_t>(blk_cnt + 1) * blk_size;
LOGI("dvd used_capacity: %{public}u * %{public}u = %{public}" PRIu64, blk_cnt + 1, blk_size, dvdUsedCapcity);
return E_OK;
}
int GetDvdConfiguration(int fd, int &dvdMedia)
{
unsigned char cmd_buf[GET_CAPACITY_CMD_BUF_LEN] = {0};
int cmd_len = GET_DVD_USED_CAPACITY_CMD_LEN;
unsigned char data_buf[GET_CAPACITY_DATA_BUF_LEN] = {0};
unsigned int data_len = GET_DVD_USED_CAPACITY_DATA_LEN;
int ret = 0;
* 使用 SCSI GET CONFIGURATION 指令 (0x46) 获取光驱设备的功能列表。
* 字段详解:
* cmd_buf[0]: 操作码 0x46 (GPCMD_GET_CONFIGURATION)。
* cmd_buf[1]: RT (Request Type) 字段。
* 设置为 1 (01b) 表示请求“当前活动的功能”(Current Features Only),
* 即仅列出当前插入介质所支持的功能(如刻录、读取能力)。
* cmd_buf[7-8]: 分配长度 (Allocation Length)。
* 指定接收配置数据(Feature Descriptors)的最大缓冲区长度。
* 高 8 位存入 cmd_buf[7],低 8 位存入 cmd_buf[8]。
* 用途:
* 常用于探测光驱是否支持特定的 Profile(如 DVD-RW, BD-RE)以及当前光盘的写入模式。
*/
cmd_buf[0] = GPCMD_GET_CONFIGURATION;
cmd_buf[1] = 1;
cmd_buf[7] = (data_len >> 8) & 0xff;
cmd_buf[8] = data_len & 0xff;
std::string cdbStr;
for (int i = 0; i < cmd_len; i++) {
cdbStr += (i > 0 ? "," : "") + std::to_string(cmd_buf[i]);
}
LOGI("[L8:DiskUtils] GetDvdConfiguration: cdb=[%{public}s]", cdbStr.c_str());
ret = SendScsiCmd(fd, cmd_buf, cmd_len, data_buf, data_len);
if (ret != 0) {
LOGE("get atip data len failed, ret val is %{public}d", ret);
return E_ERR;
}
dvdMedia = static_cast<int>((unsigned int)data_buf[DISC_TYPE_OFFSET_HIGH] << DISC_TYPE_OFFSET) |
data_buf[DISC_TYPE_OFFSET_LOW];
LOGI("dvd_media: %{public}#x", dvdMedia);
return E_OK;
}
int GetBdTotalCapacity(int fd, int64_t &bdTotalCapacity)
{
unsigned char cmd_buf[GET_CAPACITY_CMD_BUF_LEN] = {0};
int cmd_len = GET_DVD_USED_CAPACITY_CMD_LEN;
unsigned char data_buf[GET_CAPACITY_DATA_BUF_LEN] = {0};
unsigned int data_len = GET_DVD_USED_CAPACITY_DATA_LEN;
int ret = 0;
unsigned int blk_cnt = 0;
unsigned int blk_size = 0;
* 使用 SCSI READ CAPACITY 指令 (0x25) 获取蓝光光盘容量信息。
* 字段详解:
* cmd_buf[0]: 操作码 0x25 (GPCMD_READ_CDVD_CAPACITY)。
* cmd_buf[7-8]: 分配长度 (Allocation Length)。
* 返回数据:
* data_buf[0-3]: 最后逻辑块地址 (Last LBA)。
* data_buf[4-7]: 块大小 (Block Length)。
*/
cmd_buf[0] = GPCMD_READ_CDVD_CAPACITY;
cmd_buf[CDB_ALLOCATION_LENGTH_HIGH] = (data_len >> BYTE_SHIFT_8) & BYTE_MASK;
cmd_buf[CDB_ALLOCATION_LENGTH_LOW] = data_len & BYTE_MASK;
ret = SendScsiCmd(fd, cmd_buf, cmd_len, data_buf, data_len);
if (ret != 0) {
LOGE("get bd total capacity failed, ret val is %{public}d", ret);
return E_ERR;
}
blk_cnt = ((unsigned int)data_buf[LAST_LBA_BYTE_0] << BYTE_SHIFT_24) |
((unsigned int)data_buf[LAST_LBA_BYTE_1] << BYTE_SHIFT_16) |
((unsigned int)data_buf[LAST_LBA_BYTE_2] << BYTE_SHIFT_8) |
data_buf[LAST_LBA_BYTE_3];
blk_size = ((unsigned int)data_buf[BLOCK_SIZE_BYTE_0] << BYTE_SHIFT_24) |
((unsigned int)data_buf[BLOCK_SIZE_BYTE_1] << BYTE_SHIFT_16) |
((unsigned int)data_buf[BLOCK_SIZE_BYTE_2] << BYTE_SHIFT_8) |
data_buf[BLOCK_SIZE_BYTE_3];
bdTotalCapacity = static_cast<int64_t>(blk_cnt + 1) * static_cast<int64_t>(blk_size);
LOGI("bd total_capacity: %{public}u * %{public}u = %{public}" PRId64 " bytes",
blk_cnt + 1, blk_size, bdTotalCapacity);
return E_OK;
}
std::string GetScsiBusNum(const std::string &sysPath)
{
LOGD("[L3:DiskUtils] GetScsiBusNum: >>> ENTER <<<");
std::string deviceLinkPath = sysPath + "/device";
char linkTarget[PATH_MAX] = {0};
ssize_t len = readlink(deviceLinkPath.c_str(), linkTarget, sizeof(linkTarget) - 1);
if (len > 0) {
linkTarget[len] = '\0';
std::string linkStr(linkTarget);
size_t lastSlash = linkStr.find_last_of('/');
if (lastSlash != std::string::npos && lastSlash + 1 < linkStr.length()) {
std::string scsiAddr = linkStr.substr(lastSlash + 1);
if (scsiAddr.find(':') != std::string::npos) {
LOGD("[L3:DiskUtils] GetScsiBusNum: SCSI_BUS_NUM=%{public}s", scsiAddr.c_str());
return scsiAddr;
}
}
}
LOGD("[L3:DiskUtils] GetScsiBusNum: <<< EXIT NOT FOUND <<< (empty)");
return "";
}
std::string GetOddDriverType(const std::string &sysPath)
{
LOGD("[L3:DiskUtils] GetOddDriverType: >>> ENTER <<<");
if (sysPath.find("usb") != std::string::npos) {
LOGD("[L3:DiskUtils] GetOddDriverType: ODD_DRIVER_TYPE=usb-storage");
return "usb-storage";
} else if (sysPath.find("sata") != std::string::npos) {
LOGD("[L3:DiskUtils] GetOddDriverType: ODD_DRIVER_TYPE=AHCI");
return "AHCI";
}
LOGD("[L3:DiskUtils] GetOddDriverType: <<< EXIT SUCCESS <<< (empty)");
return "";
}
}
}