/**
* Copyright (c) 1996-2016 TP-Link Systems Inc. All rights reserved.
* by Wang Xiaowei <wangxiaowei@tp-link.net>
*
* Core logic for multiple disk management. A disk is a general name for multiple
* storage devices which can be mounted on OS filesystem, e.g. SD card or Micro SD
* (a.k.a. TF Card), NAS, hard disk, SSD disk etc. This module mainly concerns multi
* disk scheduling, formating and status monitor.
*/
#define _LARGEFILE64_SOURCE
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/vfs.h>
#include <sys/mount.h>
#include <linux/fs.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>
#include <sys/socket.h>
#include <dirent.h>
#include "storage.h"
#include "disk.h"
#include "file.h"
#include "disk_list.h"
#include "index_file_ops.h"
#include "event.h"
#include "utils.h"
#include "ds.h"
#include "calendar.h"
#include "data_partition.h"
#include "partition_ops.h"
#include "interface.h"
#include "stm_interface.h"
#include "shmem.h"
#include "base.h"
#include "data_partition.h"
#include "entry_cache.h"
#include "stm_event.h"
#include "file_ops.h"
#include "file_spec.h"
#include "sys_info.h"
#include "media_util/mbuffer.h"
#include "media_util/avbasicop.h"
#include "tp_sync_shm.h"
#define MAX_SIMULATED_PERCENT 40
#define SIMULATED_PERCENT_INCREMENT 2
#define DONNT_CHECK_SD_INFO "B8190E976D872F98"
void set_disk_status(struct disk *disk, uint32_t status)
{
STM_INFO("disk->status from %u(%s) to %u(%s)", disk->status, disk_status_to_string(disk->status),
status, disk_status_to_string(status));
disk->status = status;
}
void set_disk_detect_status(struct disk *disk, uint32_t detect_status)
{
STM_INFO("disk->detect_status from %u(%s) to %u(%s)", disk->detect_status, disk_detect_status_to_string(disk->detect_status),
detect_status, disk_detect_status_to_string(detect_status));
disk->detect_status = detect_status;
}
void sd_send_exception_msg(EXCEPTION_EVENT_TYPE type)
{
EXCEPTION_STATUS_MSG msg = {0};
if (type < SD_EXCEPTION_MIN || type >= SD_EXCEPTION_MAX)
{
STM_INFO("invalid sd exception type:%d", type);
return;
}
STM_INFO("exception event type:%d happened.", type);
msg.type = type;
msg.status = 1;
msg_send(EXCEPTION_STATUS_MID, (U8 *)&msg, sizeof(msg));
}
void init_disk(struct disk *disk)
{
if (!disk)
return;
disk->total_space = 0;
disk->formated_space = 0;
disk->simulated_percent = 0;
disk->refcnt = 0;
disk->mode = DISK_MODE_RDONLY;
#ifdef TP_TAPO_BATTERY_CAM
disk->status = DISK_STATUS_RECOGNIZING;
#else
disk->status = DISK_STATUS_NONE;
#endif
disk->detect_status = DISK_DETECT_STATUS_NONE;
disk->write_protect = WRITE_PROTECT_STATUS_FALSE;
disk->detection.last_active = time_since_startup();
disk->detection.sock = -1;
}
void reset_disk(struct disk *disk)
{
if (disk)
{
memset(disk, 0x00, sizeof(struct disk));
disk->vlist_entry.prev = DISK_ID_ANY;
disk->vlist_entry.next = DISK_ID_ANY;
disk->id = DISK_ID_ANY;
disk->detection.sock = -1;
}
}
void reset_disk_detect_socket(struct disk *disk)
{
if (disk && disk->detection.sock >= 0)
{
shutdown(disk->detection.sock, SHUT_RDWR);
close(disk->detection.sock);
disk->detection.sock = -1;
}
}
void init_disk_partitions(struct disk *disk)
{
enum storage_data_type type = 0;
struct partition *partition = NULL;
if (!disk || disk->id > MAX_DISK_NUM)
return;
FOREACH_STORAGE_TYPE(type)
{
partition = get_partition(disk->id, type);
if (!partition)
{
continue;
}
if (STORAGE_TYPE_VIDEO == type)//only enable video data
{
init_partition(partition, true);
}
else{
init_partition(partition, false);
}
}
}
int get_disk_space(struct disk *disk, uint64_t *total_space)
{
FILE *pf = NULL;
const struct partition_spec *spec = NULL;
char str_total[32] = {0};
int ret = 0;
int sd_dev = -1;
off_t actual_space = 0;
struct statfs sdinfo = {0};
if (!disk || !total_space)
return -1;
/*the $2 is disk's all space, the $4 is disk's avalible space
*when use sd card, we get the all space; NAS need to use the avalible space
*/
if (disk->mnt_info.type == DISK_TYPE_LOCAL)
{
/* 时间优化:优先使用系统调用方式获取sd卡容量信息 */
if (statfs(disk->mnt_info.mount_dir, &sdinfo) == 0)
{
*total_space = sdinfo.f_bsize * sdinfo.f_blocks;
goto get_space_done;
}
if ((sd_dev = open(disk->mnt_info.disk_dir, O_EXCL | O_RDWR)) < 0)
{
pf = formatted_popen("df -P %s | sed \'1d\' | awk \'{print $2}\'", disk->mnt_info.mount_dir);
}
else
{
actual_space = lseek(sd_dev, 0, SEEK_END);
close(sd_dev);
/* 减去FAT32表(2张)占用的空间,为实际可用空间 */
*total_space = (uint64_t)(actual_space - actual_space / (4 * 1024));
goto get_space_done;
}
}
else
{
pf = formatted_popen("df -P %s | sed \'1d\' | awk \'{print $4}\'", disk->mnt_info.mount_dir);
}
if (!pf)
{
STM_ERROR("get disk size failed: %s", strerror(errno));
ret = -1;
goto out;
}
if (fscanf(pf, "%s", str_total) != 1)
{
STM_ERROR("%s", strerror(errno));
ret = -1;
goto out;
}
/* the unit of the value we get from df command is KB, change to B */
*total_space = KB_TO_BYTE(atoll(str_total));
pclose(pf);
pf = NULL;
get_space_done:
/* get the space of datadir for remote disk. */
if (disk->mnt_info.type == DISK_TYPE_REMOTE)
{
memset(str_total, 0, sizeof(str_total));
pf = formatted_popen("du -s %s/%s", disk->mnt_info.mount_dir, REMOTE_DATA_NAME);
if (pf && (fscanf(pf, "%s", str_total) == 1))
{
*total_space += KB_TO_BYTE(atoll(str_total));
}
}
spec = get_partition_spec(STORAGE_TYPE_VIDEO);
if (NULL == spec)
{
STM_ERROR("get spec failed.");
ret = -1;
goto out;
}
/*leave one video size space in disk to prevent the
*unexpect bugs occur in the furture
*/
if (*total_space < spec->data_size)
{
*total_space = 0UL;
}
else
{
*total_space -= spec->data_size;
}
out:
if (pf)
{
pclose(pf);
pf = NULL;
}
return ret;
}
static int mkdir_p(const char *path)
{
char tmp[512];
strncpy(tmp, path, sizeof(tmp)-1);
tmp[sizeof(tmp)-1] = '\0'; // 确保字符串终止
size_t len = strlen(tmp);
if (len > 0 && tmp[len-1] == '/') tmp[--len] = '\0'; // 去除末尾斜杠
for (char *p = tmp + 1; *p; p++)
{
if (*p == '/')
{
*p = '\0'; // 临时截断路径
if (access(tmp, F_OK) && mkdir(tmp, 0755))
{
*p = '/'; // 恢复路径结构
return -1;
}
*p = '/'; // 恢复路径继续处理
}
}
// 创建最末级目录
return (access(tmp, F_OK) && mkdir(tmp, 0755)) ? -1 : 0;
}
static void build_mount_options(char* mount_options, int size)
{
const char* str_usefree = "usefree";
int option_len = 0;
#ifdef CONFIG_TARGET_ssc30x_ssc308
const char* str_errors = ",errors=continue";
int buf_used = 0;
#endif
if (NULL == mount_options || size <= 0)
{
STM_ERROR("Invalid Params!!!");
return;
}
memset(mount_options, 0, size);
option_len = strlen(str_usefree);
if (option_len > (size - 1))
{
STM_ERROR("the size of mount_options is too small!!!");
return;
}
strncpy(mount_options, str_usefree, size - 1);
#ifdef CONFIG_TARGET_ssc30x_ssc308
buf_used = option_len;
option_len = strlen(str_errors);
if (option_len > (size - buf_used - 1))
{
STM_ERROR("mount_options too small! Cannot add option errors=continue");
return;
}
strncat(mount_options + buf_used, str_errors, size - buf_used - 1);
#endif
return;
}
/**
* FIXME: hard to get fail reason when mount failed.
*/
int do_mount(struct disk *disk, uint32_t timeout_ms)
{
int ret = 0;
char *type = NULL;
char cmd_buf[64];
int mount_times = 0;
char mount_options[32] = {0};
if (!disk)
return -1;
if (DISK_TYPE_LOCAL == disk->mnt_info.type)
{
if (access(disk->mnt_info.mount_dir, F_OK) == 0)
{
ret = umount2(disk->mnt_info.mount_dir, MNT_FORCE);
if (ret != 0)
{
STM_ERROR("umount2(%s) fail! ret(%d) errno(%d-%s)", disk->mnt_info.mount_dir, ret, errno, strerror(errno));
/* errno(16-Device or resource busy) */
if (errno == EBUSY)
{
memset(cmd_buf, 0, sizeof(cmd_buf));
/* -v显示详细信息,-l先取消对文件系统的引用待所有在使用文件系统的进程退出后才卸载,-f强制卸载 */
snprintf(cmd_buf, sizeof(cmd_buf), "umount -vlf %s", disk->mnt_info.mount_dir);
ret = system(cmd_buf);
if (ret != 0)
{
STM_ERROR("umount failed finally. ret(%d) errno(%d-%s)", ret, errno, strerror(errno));
}
}
}
else
{
STM_INFO("umount2(%s) success!", disk->mnt_info.mount_dir);
}
}
type = "sdcard";
/*从代码逻辑来看,这里是在处理sd卡插入事件时挂载sd卡,可以直接挂载,无需调用脚本*/
mkdir_p(disk->mnt_info.mount_dir);
build_mount_options(mount_options, sizeof(mount_options));
ret = mount(disk->mnt_info.disk_dir, disk->mnt_info.mount_dir, "vfat", 0, mount_options);
if (0 != ret)
{
for (mount_times = 0; mount_times < 3; mount_times++)
{
ret = formatted_exec("mount_disk mount harddisk_%u %u %s %s %s", disk->id, timeout_ms, type,
disk->mnt_info.disk_dir, disk->mnt_info.mount_dir);
STM_ERROR("mount sdcard(%ums):%x, errno(%d-%s)", tp_sys_get_real_boot_time(), ret, errno, strerror(errno));
if (ret == 0)
{
break;
}
}
}
}
else
{
if (DISK_FSTYPE_CIFS == disk->mnt_info.fstype)
{
type = "cifs";
}
else if (DISK_FSTYPE_NFS == disk->mnt_info.fstype)
{
type = "nfs";
}
else
{
return -1;
}
ret = formatted_exec("mount_disk mount harddisk_%u %u %s %s %s %s %s", disk->id, timeout_ms, type,
disk->mnt_info.disk_dir, disk->mnt_info.mount_dir, disk->mnt_info.username, disk->mnt_info.password);
}
if (!ret)
{
set_disk_online_status(disk, disk->id, true, false);
}
return ret;
}
int do_umount(struct disk *disk, bool force)
{
int ret = 0;
const char *force_opt = force ? "-f" : "";
if (!disk || !strlen(disk->mnt_info.mount_dir))
return -1;
/* Do not call mount_disk umount disk_%u, which will fail with
* luci having deleted uci section __before__ calling ubus.
* Read mount dir from memory instead.
*/
ret = formatted_exec("umount %s %s", force_opt, disk->mnt_info.mount_dir);
if(ret != 0)
{
STM_ERROR("umount failed");
}
set_disk_online_status(disk, disk->id, false, false);
return ret;
}
/* clean up all things in disk */
static int do_format(struct disk *disk)
{
int ret = 0;
char data_path[STM_PATH_MAX] = {0};
if (!disk)
return -1;
switch (disk->mnt_info.type)
{
case DISK_TYPE_LOCAL:
/* FIXME: Who takes the responsibility to do umount should
* also has responsibility to do mount.
*/
ret = formatted_exec("fdisk_sd_card %s %s", SD_CARD_DEV_NAME, disk->mnt_info.mount_dir);
if (ret != 0)
{
sd_send_exception_msg(SD_UNFORMAT_SCRIPT_RUN_ERROR);
STM_ERROR("%s", strerror(errno));
return -1;
}
break;
case DISK_TYPE_REMOTE:
get_data_path(&disk->mnt_info, data_path, sizeof(data_path));
/* Remove all files in datadir if datadir exists. */
if (0 == access(data_path, F_OK))
{
return formatted_exec("rm -rf %s", data_path);
}
break;
default:
ret = -1;
break;
}
return ret;
}
void add_disk_to_list(struct disk *disk)
{
if (!disk)
return;
if (disk->status != DISK_STATUS_NORMAL && disk->status != DISK_STATUS_FULL)
{
return;
}
disk_list_add_tail(disk->id);
}
void delete_disk_from_list(struct disk *disk)
{
if (!disk)
return;
disk_list_del(disk->id);
}
/* For local disk, the data_path is its mount_dir;
* For remote disk, the data_path is "mount_dir" + "/" + REMOTE_DATA_NAME.
*/
int get_data_path(struct mount_info *mount, char *path, size_t path_len)
{
size_t len = 0;
if (!mount || !path)
return -1;
len = strlen(mount->mount_dir);
if (mount->type == DISK_TYPE_REMOTE)
{
len += (1 + strlen(REMOTE_DATA_NAME));
if (snprintf(path, path_len, "%s/%s", mount->mount_dir, REMOTE_DATA_NAME) != (int)len)
{
STM_ERROR("%s", strerror(errno));
return -1;
}
}
else
{
if (snprintf(path, path_len, "%s", mount->mount_dir) != (int)len)
{
STM_ERROR("%s", strerror(errno));
return -1;
}
}
return 0;
}
/**
* Whether currently-written-on disk should be replaced by new disk:
* 1. previous disk is local SD card and new disk is remotely mounted(NAS or FTP).
* SD card has limited write times, so use remote disks with higher priority.
* 2. the current disk has been exhausted.
*/
static bool
should_disk_be_replaced_for_partition(struct disk *replace, struct disk *prev,
enum storage_data_type type)
{
bool should_replace = false;
uint32_t prev_disk_type = 0;
uint32_t new_disk_type = 0;
struct partition *prev_partition = NULL;
struct partition *new_partition = NULL;
const struct partition_ops *ops = NULL;
bool overlap_enabled = false;
if (!replace || replace->mode == DISK_MODE_RDONLY)
{
return false;
}
/* Special occasion: new one SHOULD replace NULL */
if (!prev)
{
return true;
}
/* prev disk has been invalid, replace it */
if (prev->id == DISK_ID_ANY)
{
return true;
}
ops = get_partition_ops(type);
if (ops->is_storage_type_overlap_enabled)
{
overlap_enabled = ops->is_storage_type_overlap_enabled();
}
prev_disk_type = prev->mnt_info.type;
new_disk_type = replace->mnt_info.type;
prev_partition = get_partition(prev->id, type);
new_partition = get_partition(replace->id, type);
if (!prev_partition || !new_partition)
{
STM_ERROR("get partition failed");
return false;
}
should_replace |= (prev_disk_type == DISK_TYPE_LOCAL && new_disk_type == DISK_TYPE_REMOTE &&
(overlap_enabled == true || new_partition->space_info.avail != 0));
should_replace |= (prev_disk_type == new_disk_type && prev_partition->space_info.avail == 0);
STM_INFO("prev id = %d, disk id = %d, replace = %d", prev->id, replace->id, should_replace);
return should_replace;
}
/**
* Try to assign new @disk to replace currently accessed disk in runtime context
* for storage partition of @type(e.g. video or pic).
*/
int try_set_cur_disk_for_type(struct disk *disk, enum storage_data_type type)
{
struct disk *prev_disk = NULL;
uint32_t cur_disk = get_shm_cur_disk(type);
if (!disk)
{
STM_ERROR("disk is null");
return -1;
}
prev_disk = get_disk_by_id(cur_disk);
/* if current disk should not be replaced, skip and exit */
if (!should_disk_be_replaced_for_partition(disk, prev_disk, type))
{
return 0;
}
set_shm_cur_disk(type, disk->id);
return 0;
}
int set_curr_disk(struct disk *disk)
{
int ret = 0;
enum storage_data_type type = 0;
if (!disk)
{
return -1;
}
FOREACH_STORAGE_TYPE(type)
{
ret = try_set_cur_disk_for_type(disk, type);
if (ret != 0)
{
return ret;
}
}
return 0;
}
int get_disk_all_partition_info(struct disk *disk)
{
int ret = 0;
enum storage_data_type type = 0;
char index_filepath[STM_PATH_MAX] = {0};
struct partition *partition = NULL;
struct system_info *sys_info = NULL;
struct index_file_ops *index_fop = NULL;
if (!disk)
{
STM_ERROR("disk is null");
return -1;
}
sys_info = get_sys_info(disk->id);
if (!sys_info)
{
STM_ERROR("get sys info failed");
return -1;
}
index_fop = get_index_fop(sys_info->index_file_type);
FOREACH_STORAGE_TYPE(type)
{
#ifdef HEATMAP_SUPPORT
if (type == STORAGE_TYPE_HEATMAP)
{
continue;
}
#endif
#ifdef PASSENGER_FLOW_SUPPORT
if (type == STORAGE_TYPE_PASSENGER_FLOW)
{
continue;
}
#endif
if (!if_storage_type_supported(type))
{
continue;
}
partition = get_partition(disk->id, type);
if (!partition)
{
continue;
}
memset(index_filepath, 0, sizeof(index_filepath));
index_fop->get_index_file_path(disk, type, index_filepath, sizeof(index_filepath));
/* If partition read or checksum fail, try to replace with backup index. */
ret = index_fop->read_partition_info(disk, index_filepath, type, partition);
if (ret)
{
set_disk_status(disk, DISK_STATUS_BROKEN);
return -1;
}
if (partition->space_info.avail < 0)
{
partition->space_info.avail = 0;
}
}
return 0;
}
/**
* Check if @disk is busy when being occupied at least ONE data partition.
*/
bool is_disk_busy(struct disk *disk)
{
struct list_head *head = get_disk_write_fd_list_head(disk->id);
return list_empty(head);
}
/**
* Search ALL disks and return the first match with available space for data @type.
*/
static struct disk *
get_first_available_disk(struct vlist_head *disk_entry, enum storage_data_type type)
{
uint32_t id = DISK_ID_ANY;
struct disk *iter = NULL;
struct partition *partition = NULL;
if (!disk_entry)
{
return NULL;
}
/* search the disk with enough unused space */
DISK_LIST_FOREACH_POS(id, disk_entry->next)
{
iter = get_disk_by_id(id);
partition = get_partition(iter->id, type);
if (!partition)
{
STM_ERROR("get partition failed");
continue;
}
/* Only disk with following properties could be used:
* 1. not full;
* 2. with normal status;
* 3. has write permission;
* 4. online;
*/
if (partition->space_info.avail != 0 && iter->status == DISK_STATUS_NORMAL &&
iter->mode == DISK_MODE_RDWR && iter->is_online == true)
{
return iter;
}
}
return NULL;
}
/**
* Search the first overlap enabled disk.
*/
static struct disk *
get_next_disk_for_overlap(struct disk *disk, struct vlist_head *head, enum storage_data_type type)
{
struct partition *partition = NULL;
struct disk *new_disk = NULL;
const struct partition_ops *ops = NULL;
bool overlap_enabled = false;
if (!disk || !head)
{
return NULL;
}
ops = get_partition_ops(type);
if (ops->is_storage_type_overlap_enabled)
{
overlap_enabled = ops->is_storage_type_overlap_enabled();
}
if (!overlap_enabled)
{
return NULL;
}
partition = get_partition(disk->id, type);
if (!partition)
{
STM_ERROR("get partition failed");
return NULL;
}
/* If we are overlapping, start search from the current disk */
if (partition->overlap_status == OVERLAPPING && disk->vlist_entry.next != DISK_ID_ANY)
{
new_disk = get_disk_by_id(disk->vlist_entry.next);
}
/* else search from the head. */
else
{
new_disk = get_disk_by_id(head->next);
}
while (new_disk)
{
if ((new_disk->status == DISK_STATUS_NORMAL || new_disk->status == DISK_STATUS_FULL) &&
new_disk->mode == DISK_MODE_RDWR && new_disk->is_online == true)
{
return new_disk;
}
else
{
new_disk = get_disk_by_id(new_disk->vlist_entry.next);
continue;
}
}
return NULL;
}
int get_overlap_status(struct disk *disk, enum storage_data_type type, uint32_t *overlap_status)
{
struct partition *partition = NULL;
const struct partition_ops *ops = NULL;
bool overlap_enabled = false;
if (!overlap_status)
{
STM_ERROR("invalid para");
return -1;
}
*overlap_status = NOT_OVERLAPED;
if (!disk)
{
STM_ERROR("disk is NULL");
return -1;
}
ops = get_partition_ops(type);
if (ops->is_storage_type_overlap_enabled)
{
overlap_enabled = ops->is_storage_type_overlap_enabled();
}
if (!overlap_enabled)
{
STM_DEBUG("overlap disabled");
*overlap_status = NOT_OVERLAPED;
return 0;
}
partition = get_partition(disk->id, type);
if (!partition)
{
STM_ERROR("get partition failed");
return -1;
}
*overlap_status = partition->overlap_status;
return 0;
}
/* 定码率:1024/8 = 128 */
#define MAIN_STRM_BITRATE_RATIO_CBR 128
/* 变码率:(1024/8) * 32% ~= 40 */
#define MAIN_STRM_BITRATE_RATIO_VBR 40
int get_record_free_duration(uint64_t free_space, uint32_t *rcd_free_dur)
{
uint32_t br_ratio = MAIN_STRM_BITRATE_RATIO_VBR;
uint32_t bitrate = 0;
uint8_t bitrate_type = 0;
unsigned int sample_rate = AUDIO_SAMPLING_RATE_8000HZ;
if (!rcd_free_dur)
{
STM_ERROR("invalid para");
return -1;
}
*rcd_free_dur = 0;
if (free_space <= 0)
{
STM_DEBUG("no more free_space");
return 0;
}
if (ds_read_video_cfg(&bitrate, &bitrate_type) != 0)
{
STM_ERROR("read video uci config failed");
return -1;
}
STM_DEBUG("video br=%u, br_type=%d", bitrate, bitrate_type);
if (bitrate <= 0)
{
STM_ERROR("valid video main stream bitrate");
return -1;
}
br_ratio = (1 == bitrate_type) ?
MAIN_STRM_BITRATE_RATIO_CBR : MAIN_STRM_BITRATE_RATIO_VBR;
/*
* 音频码率 = 音频采样率 * 声道数 * 每个采样点所占字节数 * 压缩率
* 音频采样率: PCM a-low: 8000HZ, PCM u-low: 16000HZ;
* 声道数:分为左右两个声道,目前IPC采用单声道,故声道数为1;
* 每个采样点所占字节数: 一般为2Byte;
* 压缩率:目前IPC采用PCM a/u-low压缩算法,压缩率为50%;
* 故 音频码率 = AUDIO_SAMPLE_RATE * 1 * 2 * 50% = AUDIO_SAMPLE_RATE
*/
ds_read_microphone_cfg(&sample_rate, NULL);
STM_DEBUG("br_ratio=%u, audio_br=%u", br_ratio, GetADTSSampleRate(sample_rate));
/* 可录制时长 = 磁盘剩余空间 / (主码流码率 * 码率比例 + 音频码率) */
*rcd_free_dur = free_space / (bitrate * br_ratio + GetADTSSampleRate(sample_rate));
return 0;
}
/**
* Update partition of @type runtime current disk. Context current disk will use the first
* available disk if possible. If all disks are full, then try to get an overlap-enabled
* disk.
*
* @disk: supposed to be current disk
* @force_new_update: force to switch to a __different__ disk(e.g. deletion require previous
* one released).
*/
int update_cur_disk(struct disk *disk, enum storage_data_type type, bool force_new_update)
{
struct partition *partition = NULL;
struct disk *new_disk = NULL;
uint32_t cur_disk = get_shm_cur_disk(type);
struct vlist_head *local_head = get_list_head(DISK_TYPE_LOCAL);
struct vlist_head *remote_head = get_list_head(DISK_TYPE_REMOTE);
if (!local_head || !remote_head)
{
return -1;
}
if (!disk)
{
/* A special case, no disk in shmem, only when overlap enable status changes from
* disable to enable would go to this branch. */
new_disk = get_next_disk_for_overlap(get_disk_by_id(remote_head->next), remote_head, type);
if (new_disk && !(force_new_update && (cur_disk == new_disk->id)))
{
goto set_overlap_status;
}
new_disk = get_next_disk_for_overlap(get_disk_by_id(local_head->next), local_head, type);
if (new_disk && !(force_new_update && (cur_disk == new_disk->id)))
{
goto set_overlap_status;
}
set_shm_cur_disk(type, DISK_ID_ANY);
return -1;
}
/* Ignore */
if (disk->id != cur_disk)
{
return 0;
}
/* FIXME: the process is redundant and yet to be optimized */
switch (disk->mnt_info.type)
{
case DISK_TYPE_LOCAL:
new_disk = get_first_available_disk(remote_head, type);
/* When delete disk, ignore the valid disk which is the delete disk. */
if (new_disk && !(force_new_update && (cur_disk == new_disk->id)))
{
goto success;
}
new_disk = get_next_disk_for_overlap(get_disk_by_id(remote_head->next), remote_head, type);
if (new_disk && !(force_new_update && (cur_disk == new_disk->id)))
{
goto set_overlap_status;
}
/* search the local disk list */
new_disk = get_first_available_disk(&disk->vlist_entry, type);
if (new_disk && !(force_new_update && (cur_disk == new_disk->id)))
{
goto success;
}
new_disk = get_next_disk_for_overlap(disk, local_head, type);
if (new_disk && !(force_new_update && (cur_disk== new_disk->id)))
{
goto set_overlap_status;
}
break;
case DISK_TYPE_REMOTE:
/* search the remote disk list */
new_disk = get_first_available_disk(&disk->vlist_entry, type);
/* When delete disk, ignore the valid disk which is the delete disk. */
if (new_disk && !(force_new_update && (cur_disk == new_disk->id)))
{
goto success;
}
new_disk = get_next_disk_for_overlap(disk, remote_head, type);
if (new_disk && !(force_new_update && (cur_disk == new_disk->id)))
{
goto set_overlap_status;
}
/* search the local disk list */
new_disk = get_first_available_disk(local_head, type);
if (new_disk && !(force_new_update && (cur_disk == new_disk->id)))
{
goto success;
}
new_disk = get_next_disk_for_overlap(get_disk_by_id(local_head->next), local_head, type);
if (new_disk && !(force_new_update && (cur_disk == new_disk->id)))
{
goto set_overlap_status;
}
break;
default:
break;
}
set_shm_cur_disk(type, DISK_ID_ANY);
return -1;
set_overlap_status:
if (new_disk)
{
partition = get_partition(new_disk->id, type);
if (!partition)
{
STM_ERROR("get partition failed");
return -1;
}
partition->overlap_status = OVERLAPPING;
}
success:
if (new_disk)
{
set_shm_cur_disk(type, new_disk->id);
}
return 0;
}
/* 1. Set the oldest available file as current file, and set its offset as 0.
* 2. Clean up the index entries of current file.
* 3. Set disk unused space.
* 4. Set disk start time.
*/
int do_overlap_photo(struct disk *disk, struct stm_file *file)
{
int ret = 0;
struct partition *partition = NULL;
const struct partition_ops *ops = NULL;
const struct partition_spec *spec = NULL;
struct index_file_ops *index_fop = NULL;
struct system_info *sys_info = NULL;
int file_id = 0;
INDEX_BLOCK_TYPE block_type;
int file_num = 0;
if (!disk || !file)
{
return -1;
}
sys_info = get_sys_info(disk->id);
if (!sys_info)
{
return -1;
}
ops = get_partition_ops(file->type);
partition = get_partition(disk->id, file->type);
if (!partition)
{
return -1;
}
spec = get_partition_spec(file->type);
index_fop = get_index_fop(sys_info->index_file_type);
/* reset disk and assign all disk available for writing */
/* 消息推送图片循环覆盖,要求先覆盖最旧的;如果别的类型有新的循环覆盖需求,则对应再添加do_overlap实现 */
/* current index block data only support file loop coverage by order,
* use get oldest pic file may collapse the index block data structure.
*/
file_num = partition->files_info.data_file_num;
for (file_id = 0; file_id < file_num; file_id++)
{
if (!check_skip_curr_file(disk->id, file->type, file_id, 0, NULL))
{
break;
}
}
if (file_id >= file_num)
{
STM_ERROR("file id invalid");
return -1;
}
set_disk_status(disk, DISK_STATUS_NORMAL);
ret = open_storage_ctx_fd_photo(file, O_RDWR, 0);
if (ret != 0)
{
STM_WARN("fail to open storage context fd.");
return -1;
}
partition->cur_file_id = 0;
prepare_index_block_data_for_current_file(file, file_id);
partition->file_in_use[file->channel] = file_id;
partition->cur_file_id = file_id + 1;
partition->overlap_time = 0;
file->writer.file_id = file_id;
inc_disk_refcnt(file->writer.disk_id);
/* reset data file offset */
ret = lseek(file->fd, 0, SEEK_SET);
if (ret < 0)
{
STM_WARN("%s", strerror(errno));
return -1;
}
disk_write_fd_list_lock();
STM_INFO("add file to disk[%d] list", disk->id);
list_add(&file->list_node, get_disk_write_fd_list_head(disk->id));
disk_write_fd_list_unlock();
/* Clean up the index entries of current file and set start time. */
reset_file_info(&file->writer.file_entry);
file->writer.dirty = false;
/* clear clear event entries before clear index block data */
ret = index_fop->clear_all_entries_in_file(disk, file, file->writer.file_id);
if (ret != 0)
{
STM_WARN("fail to handle overlapped file.");
return ret;
}
/* clear index block data, TODO: simplize the operation */
FOREACH_BLOCK_TYPE(block_type)
{
if (if_block_type_supported_of_storge_type(file->type, block_type))
{
STM_ERROR("clear index block data disk %d, file %d, block_type %d", file->writer.disk_id,
file->writer.file_id, block_type);
clear_index_block_data_by_block_type(file->writer.disk_id, file->writer.file_id, block_type);
}
}
if (ops->write_index_to_disk)
{
lock_entry_cache(file->channel, file->type);
ops->write_index_to_disk(file, false);
unlock_entry_cache(file->channel, file->type);
}
/* update the calendar index */
if (spec->has_calendar_idx)
{
rebuild_cal_index(file);
}
return 0;
}
/* 1. Set the first available file as current file, and set its offset as 0.
* 2. Clean up the index entries of current file.
* 3. Set disk unused space.
* 4. Set disk start time.
*/
int do_overlap(struct disk *disk, struct stm_file *file)
{
int ret = 0;
struct partition *partition = NULL;
const struct partition_ops *ops = NULL;
const struct partition_spec *spec = NULL;
struct index_file_ops *index_fop = NULL;
struct system_info *sys_info = NULL;
int file_id = 0;
int file_num = 0;
INDEX_BLOCK_TYPE block_type;
if (!disk || !file)
{
STM_ERROR("disk or file is null");
return -1;
}
sys_info = get_sys_info(disk->id);
if (!sys_info)
{
STM_ERROR("get sys info failed");
return -1;
}
ops = get_partition_ops(file->type);
partition = get_partition(disk->id, file->type);
if (!partition)
{
STM_ERROR("get partition failed");
return -1;
}
spec = get_partition_spec(file->type);
index_fop = get_index_fop(sys_info->index_file_type);
/* reset disk and assign all disk available for writing */
file_num = partition->files_info.data_file_num;
for (file_id = 0; file_id < file_num; file_id++)
{
if (!check_skip_curr_file(disk->id, file->type, file_id, 0, NULL))
{
break;
}
}
if (file_id >= file_num)
{
STM_ERROR("file id %d > %d", file_id, file_num);
set_disk_status(disk, DISK_STATUS_BROKEN);
sd_send_exception_msg(SD_BROKEN_NO_DATA);
return -1;
}
set_disk_status(disk, DISK_STATUS_NORMAL);
#ifdef USING_MP4_STORAGE
ret = open_storage_ctx_fd(file, O_RDWR, 1);
#else
ret = open_storage_ctx_fd(file, O_RDWR);
#endif
if (ret != 0)
{
STM_WARN("fail to open storage context fd.");
return -1;
}
partition->cur_file_id = 0;
prepare_index_block_data_for_current_file(file, file_id);
partition->file_in_use[file->channel] = file_id;
partition->cur_file_id = file_id + 1;
partition->overlap_time = 0;
file->writer.file_id = file_id;
inc_disk_refcnt(file->writer.disk_id);
#ifndef NAMING_FILE_WITH_TIME
/* reset data file offset */
ret = lseek(file->fd, 0, SEEK_SET);
if (ret < 0)
{
STM_WARN("%s", strerror(errno));
return -1;
}
#endif
disk_write_fd_list_lock();
STM_INFO("add file to disk[%d] list", disk->id);
list_add(&file->list_node, get_disk_write_fd_list_head(disk->id));
disk_write_fd_list_unlock();
/* Clean up the index entries of current file and set start time. */
reset_file_info(&file->writer.file_entry);
file->writer.dirty = false;
/* clear clear event entries before clear index block data */
ret = index_fop->clear_all_entries_in_file(disk, file, file->writer.file_id);
if (ret != 0)
{
STM_WARN("fail to handle overlapped file.");
return ret;
}
/* clear index block data, TODO: simplize the operation */
FOREACH_BLOCK_TYPE(block_type)
{
if (if_block_type_supported_of_storge_type(STORAGE_TYPE_VIDEO, block_type))
{
clear_index_block_data_by_block_type(file->writer.disk_id, file->writer.file_id, block_type);
}
}
#ifdef USING_MP4_STORAGE
/* Update MP4 index first */
if (ops->write_mp4_index_to_disk)
{
ops->write_mp4_index_to_disk(file);
}
#endif
if (ops->write_index_to_disk)
{
lock_entry_cache(file->channel, file->type);
ops->write_index_to_disk(file, false);
unlock_entry_cache(file->channel, file->type);
}
/* update the calendar index */
if (spec->has_calendar_idx)
{
rebuild_cal_index(file);
}
return 0;
}
void update_overlap_disk(void)
{
enum storage_data_type type = 0;
uint32_t disk_id = DISK_ID_ANY;
struct disk *disk = NULL;
struct vlist_head *list_head = NULL;
sem_lock(CURR_DISK_SEM);
FOREACH_STORAGE_TYPE(type)
{
disk_id = get_shm_cur_disk(type);
disk = get_disk_by_id(disk_id);
/* If goes to this branch, the situation must be: the overlap enable status
* is disabled before, and change to be enabled. If shm_cur_disk is NULL, or
* shm_cur_disk is local disk but the remote disk list is not empty, then
* try to get an overlap_enabled disk to do overlap.
*/
list_head = get_list_head(DISK_TYPE_REMOTE);
if (!list_head)
{
break;
}
if (disk_id == DISK_ID_ANY ||
(disk->mnt_info.type == DISK_TYPE_LOCAL && list_head->next != DISK_ID_ANY))
{
update_cur_disk(NULL, type, false);
}
}
sem_unlock(CURR_DISK_SEM);
}
/* FIXME: it may not correct to consider the channel is free or not by file_in_use,
* because when the caller exit unexpectly, it may not set the file_in_use to invalid.
*/
static bool is_all_channel_free(struct partition *partition, enum storage_data_type type)
{
int channel = 0;
for (channel = 0; channel < MAX_CHANNEL_NUM; channel++)
{
if (partition->file_in_use[channel] != INVALID_FILE_ID)
return false;
}
return true;
}
/**
* Set partition of @disk for @type full by assign space.avail 0;
* Check other partitions of the @disk, if other is full then set @disk status FULL.
*/
int set_disk_partition_full(struct disk *disk, enum storage_data_type type)
{
struct partition *partition = NULL;
if (!disk)
{
return -1;
}
partition = get_partition(disk->id, type);
if (!partition)
{
STM_ERROR("get partition failed");
return -1;
}
/* If there is any other channel is writing this partition,
* do not set partition full.
*/
if (!is_all_channel_free(partition, type))
{
return 0;
}
partition->space_info.avail = 0;
if (check_all_partitions_full(disk->id))
{
set_disk_status(disk, DISK_STATUS_FULL);
}
return 0;
}
static inline bool check_disk_partition_space(struct disk *disk)
{
uint64_t partition_sum = 0;
all_enabled_partition_size(disk->id, &partition_sum);
return (disk->total_space >= partition_sum);
}
void check_disk_occupassion(uint32_t disk_id)
{
enum storage_data_type type = 0;
FOREACH_STORAGE_TYPE(type)
{
if (get_shm_cur_disk(type) != disk_id)
continue;
println("disk %u is used as current disk for partition %s", disk_id, partition_name(type));
// TODO: print out all subscribers responsible for writing @type.
}
}
/* ***********************************************************************
* 函数名: disk detect()
* 用途: 判断path 路径下含有subpath 子 字符串的目录是否存在。
* 返回:
* 存在: 返回0,并将路径名保存到buf 中。
* 不存在: 返回 -1 。
*************************************************************************/
int is_path_exist(char *path, char *subpath, char *buf, int buf_len)
{
DIR *parent_dirp;
struct dirent *entry;
int ret = -1;
if(path == NULL || subpath == NULL || buf == NULL || buf_len < MMC_NAME_LEN)
return -1;
if((parent_dirp = opendir(SD_INFO_PARENT_PATH)) == NULL)
{
STM_ERROR("dir can not open: %s", SD_INFO_PARENT_PATH);
return -1;
}
while((entry = readdir(parent_dirp)) != NULL)
{
//printf("%s\n", entry->d_name);
if(strncmp(entry->d_name, subpath, strlen(subpath)) == 0) //路径存在
{
strncpy(buf, entry->d_name, strlen(entry->d_name) + 1);
ret = 0;
break;
}
}
closedir(parent_dirp);
return ret;
}
int read_cid_info(char *cid_info_path, char *buf, int buf_len)
{
int cid_fd;
int ret = 0;
if(cid_info_path == NULL || buf == NULL || buf_len != CID_BUFF_SIZE)
return -1;
if((cid_fd = open(cid_info_path, O_RDONLY)) < 0)
return -1;
if(read(cid_fd, buf, CID_BUFF_SIZE - 1) != CID_BUFF_SIZE - 1)
{
ret = -1;
}
buf[CID_BUFF_SIZE - 1] = '\0';
close(cid_fd);
return ret;
}
int check_cid_info(char *cid_info, char buf_len)
{
enum disk_detect_status ret = DISK_DETECT_STATUS_NORMAL;
if(cid_info == NULL || buf_len != CID_BUFF_SIZE)
return -1;
if(cid_info[0] == '0' && cid_info[1] == '0')
ret = DISK_DETECT_STATUS_CID_ILLEGAL; // CID信息非法的SD卡
if(ret < 0 || ret >= DISK_DETECT_STATUS_NUM)
return -1;
return ret;
}
/* ***********************************************************************
* 函数名: disk detect()
* 用途: 检查sd 卡是否扩容卡, 暂时只支持单张sd 卡
* 返回: 失败返回-1,成功返回0,并将正确的status 存到ret_status 中。
* 步骤:
* 1. 在SD_INFO_PARENT_PATH 路径下查找sd 卡路径(../mmc*) 是否存在。
* 2. 若存在,读取此路径下存在的cid 寄存器信息。
* 3. 根据cid 寄存器信息进行判断。
*************************************************************************/
int disk_detect(enum disk_detect_status *ret_status)
{
char mmc_name[MMC_NAME_LEN]; //to save the mmc full name of mmc subdir
char cid_info_path[CID_INFO_PATH_LEN];
char cid_info[CID_BUFF_SIZE];
int ret = 0;
if(ret_status == NULL)
return -1;
/* SD_INFO_PARENT_PATH设为DONNT_CHECK_SD_INFO表明是普通硬盘模拟的sd卡,无需检查sd信息,直接返回 */
if (0 == strcmp(SD_INFO_PARENT_PATH, DONNT_CHECK_SD_INFO))
{
STM_ERROR("don't check SD_INFO_PARENT_PATH");
return 0;
}
if((ret = is_path_exist(SD_INFO_PARENT_PATH, "mmc", mmc_name, MMC_NAME_LEN)) != 0)
{
STM_ERROR("%s/mmc* path not exist", SD_INFO_PARENT_PATH);
sd_send_exception_msg(SD_DETECT_SD_INFO_DIR_EXCEPTION);
return -1;
}
snprintf(cid_info_path, CID_INFO_PATH_LEN, "%s%s/cid", SD_INFO_PARENT_PATH, mmc_name);
if((ret = read_cid_info(cid_info_path, cid_info, CID_BUFF_SIZE)) != 0)
{
STM_ERROR("read cid info return err");
sd_send_exception_msg(SD_DETECT_SD_INFO_DIR_EXCEPTION);
return -1;
}
if((ret = check_cid_info(cid_info, CID_BUFF_SIZE)) < 0)
{
STM_ERROR("check cid info return err");
return -1;
}
else
*ret_status = ret;
if (ret == DISK_DETECT_STATUS_CID_ILLEGAL)
{
sd_send_exception_msg(SD_DETECT_CID_ILLEGAL_EXCEPTION);
}
return 0;
}
int check_csd_info(char *csd_info, char buf_len)
{
enum disk_detect_status ret = WRITE_PROTECT_STATUS_FALSE;
if(csd_info == NULL || buf_len != CID_BUFF_SIZE)
{
return -1;
}
if((csd_info[28] & 0x01) || (csd_info[28] & 0x02))
{
ret = WRITE_PROTECT_STATUS_TRUE; // 写保护状态的的SD卡
sd_send_exception_msg(SD_DETECT_WRITE_PROTECT_EXCEPTION);
}
return ret;
}
int disk_write_protect_detect(enum write_protect_status *ret_status)
{
char mmc_name[MMC_NAME_LEN]; //to save the mmc full name of mmc subdir
char cid_info_path[CID_INFO_PATH_LEN];
char csd_info[CID_BUFF_SIZE];
int ret = 0;
if(ret_status == NULL)
{
return -1;
}
/* SD_INFO_PARENT_PATH设为DONNT_CHECK_SD_INFO表明是普通硬盘模拟的sd卡,无需检查sd信息,直接返回 */
if (0 == strcmp(SD_INFO_PARENT_PATH, DONNT_CHECK_SD_INFO))
{
STM_ERROR("don't check SD_INFO_PARENT_PATH");
return 0;
}
if((ret = is_path_exist(SD_INFO_PARENT_PATH, "mmc", mmc_name, MMC_NAME_LEN)) != 0)
{
STM_ERROR("%s/mmc* path not exist", SD_INFO_PARENT_PATH);
sd_send_exception_msg(SD_DETECT_SD_INFO_DIR_EXCEPTION);
return -1;
}
snprintf(cid_info_path, CID_INFO_PATH_LEN, "%s%s/csd", SD_INFO_PARENT_PATH, mmc_name);
if((ret = read_cid_info(cid_info_path, csd_info, CID_BUFF_SIZE)) != 0)
{
STM_ERROR("read csd info return err");
sd_send_exception_msg(SD_DETECT_SD_INFO_DIR_EXCEPTION);
return -1;
}
if((ret = check_csd_info(csd_info, CID_BUFF_SIZE)) < 0)
{
STM_ERROR("check csd info return err");
return -1;
}
else
{
*ret_status = ret;
}
return 0;
}
/* ***********************************************************************
* 函数名: disk_dilatant_detect_local()
* 用途: 通过写入读取的方式来判断是否扩容卡
* 返回: 1(扩容卡)/0(非扩容卡)
*************************************************************************/
static int disk_dilatant_detect_local(struct disk *disk)
{
int fd = -1;
int index = 0;
ssize_t ret = 0;
uint64_t total_space = 0;
off64_t offset = 0;
char *write_buf = NULL;
char *read_buf = NULL;
int err_cnt = 0;
FILE *fp = NULL;
char dilatant_check_path[STM_PATH_MAX] = {0};
long long write_distance = GB_TO_BYTE(1ULL);
if (!disk)
{
return -1;
}
posix_memalign((void **)&write_buf, DISK_SECTOR_SIZE, DISK_SECTOR_SIZE);
posix_memalign((void **)&read_buf, DISK_SECTOR_SIZE, DISK_SECTOR_SIZE);
if ((NULL == write_buf) || (NULL == read_buf))
{
STM_ERROR("malloc failed\n");
goto end;
}
if (do_format(disk) != 0)
{
STM_ERROR("execute format failed.(err info: %s)", strerror(errno));
goto end;
}
snprintf(dilatant_check_path, STM_PATH_MAX, "%s/dilatant_check", disk->mnt_info.mount_dir);
fp = fopen(dilatant_check_path, "w");
if (fp == NULL)
{
STM_ERROR("fopen [%s] failed", dilatant_check_path);
goto end;
}
fwrite("dilatant_check", sizeof("dilatant_check"), 1, fp);
fclose(fp);
memset(write_buf, 0, DISK_SECTOR_SIZE);
memset(read_buf, 0, DISK_SECTOR_SIZE);
fd = open64(SD_CARD_DEV_PATH, O_RDWR | O_DIRECT | O_LARGEFILE | O_SYNC, 0777);
if (fd < 0)
{
STM_ERROR("open64 %s failed\n", SD_CARD_DEV_NAME);
goto end;
}
if (ioctl(fd, BLKGETSIZE64, &total_space) != 0)
{
STM_ERROR("get total_space error\n");
goto end;
}
STM_INFO("total_space:%llu\n", total_space);
/* reserve one sector */
total_space -= DISK_SECTOR_SIZE;
for (offset = 0, index = 0; offset < total_space; offset += write_distance, index += 1)
{
memcpy(write_buf, &index, sizeof(index));
ret = pwrite64(fd, write_buf, DISK_SECTOR_SIZE, offset);
if (ret != DISK_SECTOR_SIZE)
{
STM_ERROR("pwrite64 offset:%lld failed\n", (long long)offset);
err_cnt++;
goto end;
}
}
drop_sys_cache(0, true);
for (offset = 0, index = 0; offset < total_space; offset += write_distance, index += 1)
{
memcpy(write_buf, &index, sizeof(index));
ret = pread64(fd, read_buf, DISK_SECTOR_SIZE, offset);
if (ret != DISK_SECTOR_SIZE)
{
STM_ERROR("pread64 offset:%lld failed\n", (long long)offset);
err_cnt++;
goto end;
}
if (memcmp(read_buf, write_buf, DISK_SECTOR_SIZE) != 0)
{
err_cnt++;
goto end;
}
}
end:
if (fd != -1)
{
close(fd);
}
if (NULL != write_buf)
{
free(write_buf);
}
if (NULL != read_buf)
{
free(read_buf);
}
STM_INFO("detect err_cnt:%d, maybe dilatant card\n", err_cnt);
return (err_cnt > 0 ? 1 : 0);
}
int disk_dilatant_detect(struct disk *disk, int *detect_status)
{
int ret = 0;
if (!disk)
{
return -1;
}
if (disk->mnt_info.type == DISK_TYPE_LOCAL)
{
ret = disk_dilatant_detect_local(disk);
if (ret == 1)
{
STM_WARN("dilatant card detected\n");
*detect_status = DISK_DETECT_STATUS_DILATANT_SUSPECT;
}
}
return 0;
}
#if (STORAGE_VER == 2)
int disk_dilatant_detect_v2()
{
int fd = -1;
int index = 0;
ssize_t ret = 0;
uint64_t total_space = 0;
off64_t offset = 0;
char *write_buf = NULL;
char *read_buf = NULL;
int err_cnt = 0;
long long write_distance = GB_TO_BYTE(1ULL);
posix_memalign((void **)&write_buf, DISK_SECTOR_SIZE, DISK_SECTOR_SIZE);
posix_memalign((void **)&read_buf, DISK_SECTOR_SIZE, DISK_SECTOR_SIZE);
if ((NULL == write_buf) || (NULL == read_buf))
{
STM_ERROR("malloc failed");
goto end;
}
memset(write_buf, 0, DISK_SECTOR_SIZE);
memset(read_buf, 0, DISK_SECTOR_SIZE);
fd = open64(SD_CARD_DEV_PATH, O_RDWR | O_DIRECT | O_LARGEFILE | O_SYNC, 0777);
if (fd < 0)
{
STM_ERROR("open64 %s failed", SD_CARD_DEV_NAME);
goto end;
}
if (ioctl(fd, BLKGETSIZE64, &total_space) != 0)
{
STM_ERROR("get total_space error");
goto end;
}
STM_INFO("total_space:%llu\n", total_space);
/* reserve one sector */
total_space -= DISK_SECTOR_SIZE;
for (offset = 0, index = 0; offset < total_space; offset += write_distance, index += 1)
{
memcpy(write_buf, &index, sizeof(index));
ret = pwrite64(fd, write_buf, DISK_SECTOR_SIZE, offset);
if (ret != DISK_SECTOR_SIZE)
{
STM_ERROR("pwrite64 offset:%lld failed", (long long)offset);
err_cnt++;
goto end;
}
}
drop_sys_cache(0, true);
for (offset = 0, index = 0; offset < total_space; offset += write_distance, index += 1)
{
memcpy(write_buf, &index, sizeof(index));
ret = pread64(fd, read_buf, DISK_SECTOR_SIZE, offset);
if (ret != DISK_SECTOR_SIZE)
{
STM_ERROR("pread64 offset:%lld failed", (long long)offset);
err_cnt++;
goto end;
}
if (memcmp(read_buf, write_buf, DISK_SECTOR_SIZE) != 0)
{
err_cnt++;
goto end;
}
}
drop_sys_cache(0, true);
end:
if (fd != -1)
{
close(fd);
}
if (NULL != write_buf)
{
free(write_buf);
}
if (NULL != read_buf)
{
free(read_buf);
}
if (err_cnt)
{
STM_ERROR("detect err_cnt:%d, maybe dilatant card", err_cnt);
}
return (err_cnt > 0 ? 1 : 0);
}
#endif
int format_disk_and_create_files(struct disk *disk)
{
int ret = 0;
uint64_t total_space = 0;
STORAGE_DISK_FORMAT_MSG disk_format_msg;
if (!disk)
{
return -1;
}
/* will enter disk format process, sendout disk format message */
memset(&disk_format_msg, 0, sizeof(disk_format_msg));
disk_format_msg.disk_id = disk->id;
disk_format_msg.disk_status = DISK_FORMAT_STATUS_BEGIN;
STM_INFO("sending STORAGE_DISK_FORMAT_MSG_MID begin");
AVTS_SEND(STORAGE_DISK_FORMAT_MSG_MID, (U8 *)&disk_format_msg, sizeof(disk_format_msg));
if (disk->mnt_info.type != DISK_TYPE_LOCAL)
{
/* Format this disk first. */
ret = do_format(disk);
disk->simulated_percent = DISK_FORMAT_PERCENT;
if (ret != 0)
{
STM_ERROR("execute format failed.(ret: %d, err info: %s)", ret, strerror(errno));
return ret;
}
}
init_disk_partitions(disk);
ret = get_disk_space(disk, &total_space);
if (ret != 0)
{
STM_ERROR("get disk space failed.");
sd_send_exception_msg(SD_UNFORMAT_SD_CAPACITY_FAIL);
ret = -1;
goto send_msg;
}
disk->total_space = total_space;
disk->mode = DISK_MODE_RDWR;
if (total_space < MIN_DISK_CAPACITY)
{
STM_ERROR("total_space illegal:%llu", total_space);
sd_send_exception_msg(SD_UNFORMAT_SD_CAPACITY_INSUFFICIENT);
set_disk_status(disk, DISK_STATUS_INSUFFICIENT);
ret = -1;
goto send_msg;
}
/* Calculate file layout parameters. */
ret = init_file_layout(disk);
if (ret != 0)
{
STM_ERROR("fail to init file layout.\n");
goto send_msg;
}
if (!check_disk_partition_space(disk))
{
STM_ERROR("disk partition space overflow(total %llu bytes).\n",
disk->total_space);
ret = -1;
goto send_msg;
}
//这里计算索引文件中各个区域的起始偏移
reset_partition_info(disk);
ret = disk_create_initial_files(disk);
if (ret != 0)
{
STM_ERROR("fail to create files.\n");
}
send_msg:
disk_format_msg.disk_status = DISK_FORMAT_STATUS_END;
STM_INFO("sending STORAGE_DISK_FORMAT_MSG_MID end");
AVTS_SEND(STORAGE_DISK_FORMAT_MSG_MID, (U8 *)&disk_format_msg, sizeof(disk_format_msg));
return ret;
}
int check_index_data_valid(struct disk *disk)
{
int err = 0;
struct index_file_ops *index_fop = NULL;
struct system_info *sys_info = NULL;
if (!disk)
{
STM_ERROR("illegal parameters.");
return 0;
}
sys_info = get_sys_info(disk->id);
if (!sys_info)
{
STM_ERROR("Failed to get system info of disk[%d].", disk->id);
return 0;
}
index_fop = get_index_fop(sys_info->index_file_type);
// check_binary_index_data
err = index_fop->check_index_data(disk, STORAGE_TYPE_ALL, true, true);
if (err)
{
return -1;
}
return 0;
}
/**
* Modify when informed disk is online, and do mounting or unmounting.
*/
void set_disk_online_status(struct disk *disk, uint32_t id, bool online, bool force_remount)
{
if (!disk || disk->is_online == online)
{
return;
}
disk->is_online = online;
if (force_remount)
{
const uint32_t mount_timeout_ms = 3000;
struct disk disk_copy;
/* if online status shifted, the disk should exec mount operation and
* be added to/removed from active disk list.
*/
if (online)
{
if (ds_read_disk_info(disk, id, NULL))
{
STM_WARN("fail to load disk %u config", id);
return;
}
/* currently hotplug netlink msg is sent when hardware
* insertion is detected, but partition block device may
* not be ready. wait with maximum of 1s, if mount still
* failed, then skip status shifting.
*/
if (do_mount(disk, mount_timeout_ms))
{
/* should not be here if online event already occurred.
* check online information timing or disk configuration.
*/
STM_WARN("online mounting failed.");
}
stm_add_disk(disk);
}
else
{
memcpy(&disk_copy, disk, sizeof(struct disk));
stm_delete_disk(id);
do_umount(&disk_copy, true);
}
}
}
struct disk *get_disk_by_id(uint32_t disk_id)
{
if (!verify_disk_id(disk_id, false))
return NULL;
return &(g_disk_manager_shm.shm_ptr->g_disks[disk_id - 1]);
}
uint32_t get_shm_cur_disk(enum storage_data_type type)
{
if (!verify_storage_type(type, false))
{
return DISK_ID_ANY;
}
return g_disk_manager_shm.shm_ptr->cur_disk[type];
}
void set_shm_cur_disk(enum storage_data_type type, uint32_t disk_id)
{
if (!verify_storage_type(type, false))
{
return;
}
g_disk_manager_shm.shm_ptr->cur_disk[type] = disk_id;
}
uint32_t get_disk_num(void)
{
return g_disk_manager_shm.shm_ptr->g_disk_num;
}
void inc_disk_num(void)
{
g_disk_manager_shm.shm_ptr->g_disk_num++;
}
void dec_disk_num(void)
{
g_disk_manager_shm.shm_ptr->g_disk_num--;
}
bool is_disk_overlap_enabled(void)
{
return g_disk_manager_shm.shm_ptr->g_enable_overlap;
}
void set_disk_overlap(bool enabled)
{
g_disk_manager_shm.shm_ptr->g_enable_overlap = enabled;
}
void inc_disk_refcnt(uint32_t disk_id)
{
struct disk *disk = get_disk_by_id(disk_id);
if (disk)
{
disk->refcnt++;
STM_DEBUG("refcnt = %d, disk id = %u", disk->refcnt, disk->id);
}
}
void dec_disk_refcnt(uint32_t disk_id)
{
struct disk *disk = get_disk_by_id(disk_id);
if (disk)
{
disk->refcnt--;
STM_DEBUG("refcnt = %d, disk id = %u", disk->refcnt, disk->id);
}
}
void handle_offline_disk(struct disk *disk)
{
enum storage_data_type type = 0;
uint32_t cur_disk = DISK_ID_ANY;
int err = 0;
if (!disk)
{
return;
}
/* If disk is offline, all file options will be blocked.
* Here we just change the current disk in shmem.
*/
sem_lock(CURR_DISK_SEM);
FOREACH_STORAGE_TYPE(type)
{
cur_disk = get_shm_cur_disk(type);
if (disk->id == cur_disk)
{
err = update_cur_disk(disk, type, true);
cur_disk = get_shm_cur_disk(type);
if (err != 0 || cur_disk == disk->id || cur_disk == DISK_ID_ANY)
{
STM_WARN("no available disk for %s.", partition_name(type));
set_shm_cur_disk(type, DISK_ID_ANY);
}
}
}
sem_unlock(CURR_DISK_SEM);
}