aboutsummaryrefslogtreecommitdiffstats
path: root/lib/fileop.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/fileop.c')
-rw-r--r--lib/fileop.c377
1 files changed, 377 insertions, 0 deletions
diff --git a/lib/fileop.c b/lib/fileop.c
new file mode 100644
index 0000000..c246bd7
--- /dev/null
+++ b/lib/fileop.c
@@ -0,0 +1,377 @@
+/**
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * @file fileop.c
+ * @brief file operation functions
+ */
+#include "libredundancyfileop.h"
+#include "fileop.h"
+#include "file-util.h"
+#include "static-configurator.h"
+#include "crc16.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+
+int refop_file_get_with_validation(const char *file, uint8_t *data, int64_t bufsize, int64_t *readsize);
+void refop_header_create(s_refop_file_header *head, uint16_t crc16value, uint64_t sizevalue);
+int refop_header_validation(const s_refop_file_header *head);
+int refop_file_test(const char *filename);
+
+/**
+ * Redundancy data write.
+ *
+ * @param [in] event sd event loop handle
+ *
+ * @return refop_error_t
+ * @retval 0 Succeeded.
+ * @retval -1 Abnormal fail. Shall not continue.
+ * @retval -2 Lager than size limit.
+ */
+int refop_new_file_write(refop_handle_t handle, uint8_t *data, int64_t bufsize)
+{
+ struct refop_halndle *hndl = (struct refop_halndle *)handle;
+ int ret = -1, fd = -1;
+ ssize_t wsize = 0;
+ uint8_t *pbuf = NULL, *pdata = NULL;
+ uint16_t crc16value = 0;
+ int new_state = 0;
+
+ if (bufsize > refop_get_config_data_size_limit() || bufsize <= 0)
+ return -2;
+
+ // Fource remove new file - success and noent is ok.
+ ret = unlink(hndl->newfile);
+ if (ret < 0) {
+ if (errno != ENOENT)
+ return -1;
+ }
+
+ // Create write buffer. To reduce sync write operation
+ pbuf = (uint8_t*)malloc(bufsize + sizeof(s_refop_file_header));
+ if (pbuf == NULL)
+ return -1;
+
+ // Create write data
+ pdata = pbuf + sizeof(s_refop_file_header);
+ memcpy(pdata, data, bufsize);
+ crc16value = crc16(0xffff, pdata, bufsize);
+
+ refop_header_create((s_refop_file_header*)pbuf, crc16value, bufsize);
+
+ fd = open(hndl->newfile, (O_CLOEXEC | O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW), (S_IRUSR | S_IWUSR));
+ if (fd < 0) {
+ // All open error couldnt recover.
+ free(pbuf);
+ return -1;
+ }
+
+ wsize = safe_write(fd, pbuf, bufsize + sizeof(s_refop_file_header));
+ if (wsize < 0) {
+ // All open error couldnt recover.
+ (void)close(fd);
+ free(pbuf);
+ return -1;
+ }
+
+ // sync and close
+ (void)fsync(fd);
+ (void)close(fd);
+ free(pbuf);
+
+ return 0;
+}
+
+
+/**
+ * Redundancy data write.
+ *
+ * @param [in] event sd event loop handle
+ *
+ * @return refop_error_t
+ * @retval 0 Succeeded.
+ * @retval -1 Abnormal fail. Shall not continue.
+ */
+int refop_file_rotation(refop_handle_t handle)
+{
+ struct refop_halndle *hndl = (struct refop_halndle *)handle;
+ int latest_state = -1, backup_state = -1;
+ int fd = -1;
+
+ //Get all file state
+ latest_state = refop_file_test(hndl->latestfile);
+ backup_state = refop_file_test(hndl->backupfile1);
+
+ if (latest_state == -2 || backup_state == -2)
+ return -1;
+
+ // Operation algorithm
+ // Current Next
+ // | latest | backup | | latest | backup |
+ // a1 | 1 | 2 | | new | 1 |
+ // a2 | 1 | x | | new | 1 |
+ // a3 | x | 2 | | new | 2 |
+ // a4 | x | x | | new | x |
+
+ // All error case of file was checked before this point such as stat check and new file create.
+ if (latest_state == 0) {
+ // a1 or a2
+ if (backup_state == 0) {
+ //a1
+ (void)unlink(hndl->backupfile1);
+ (void)rename(hndl->latestfile, hndl->backupfile1);
+ (void)rename(hndl->newfile, hndl->latestfile);
+ } else {
+ //a2
+ // nop (void)unlink(hndl->backupfile1);
+ (void)rename(hndl->latestfile, hndl->backupfile1);
+ (void)rename(hndl->newfile, hndl->latestfile);
+ }
+ } else {
+ //a3 or a4
+ if (backup_state == 0) {
+ //a3
+ // nop (void)unlink(hndl->backupfile1);
+ // nop (void)rename(hndl->latestfile, hndl->backupfile1);
+ (void)rename(hndl->newfile, hndl->latestfile);
+ } else {
+ //a4
+ // nop (void)unlink(hndl->backupfile1);
+ // nop (void)rename(hndl->latestfile, hndl->backupfile1);
+ (void)rename(hndl->newfile, hndl->latestfile);
+ }
+ }
+
+ // directry sync
+ fd = open(hndl->basedir, (O_CLOEXEC | O_DIRECTORY | O_NOFOLLOW));
+ if (fd >= 0) {
+ (void)fsync(fd);
+ (void)close(fd);
+ }
+
+ return 0;
+}
+
+/**
+ * Redundancy data write.
+ *
+ * @param [in] event sd event loop handle
+ *
+ * @return refop_error_t
+ * @retval 0 Succeeded.
+ * @retval 1 Succeeded with recover.
+ * @retval -1 Abnormal fail. Shall not continue.
+ * @retval -2 No data.
+ * @retval -3 Broaken data.
+ */
+int refop_file_pickup(refop_handle_t handle, uint8_t *data, int64_t bufsize, int64_t *readsize)
+{
+ struct refop_halndle *hndl = (struct refop_halndle *)handle;
+ int ret1 = -1, ret2 = -1;
+ int64_t ressize = 0;
+
+
+ ret1 = refop_file_get_with_validation(hndl->latestfile, data, bufsize, &ressize);
+ if (ret1 == 0) {
+ // got valid data
+ (*readsize) = ressize;
+ return 0;
+ } else if (ret1 < -1) {
+ // latest file was broaken, file remove
+ (void)unlink(hndl->latestfile);
+ }
+
+ ret2 = refop_file_get_with_validation(hndl->backupfile1, data, bufsize, &ressize);
+ if (ret2 == 0) {
+ // got valid data
+ (*readsize) = ressize;
+ return 1;
+ } else if (ret2 < -1) {
+ // latest file was broaken, file remove
+ (void)unlink(hndl->latestfile);
+ }
+
+ if (ret1 == -1 && ret2 == -1)
+ return -2;
+
+ return -3;
+}
+
+/**
+ * Target file status check
+ *
+ * @param [in] filename Target file path
+ *
+ * @return int
+ * @retval 0 Target file is available.
+ * @retval -1 No target file.
+ * @retval -2 Abnormal fail.
+ */
+int refop_file_test(const char *filename)
+{
+ struct stat sb;
+ int ret = -1;
+
+ //Check a directry
+ ret = stat(filename, &sb);
+ if (ret < 0) {
+ if (errno == ENOENT)
+ return -1;
+ else
+ return -2;
+ }
+
+ return 0;
+}
+
+/**
+ * Redundancy data write.
+ *
+ * @param [in] event sd event loop handle
+ *
+ * @return int
+ * @retval 0 succeeded.
+ * @retval -1 No file entry.
+ * @retval -2 Invalid file size.
+ * @retval -3 Invalid header.
+ * @retval -4 Abnomal request size (smaller than real size)
+ * @retval -5 Invalid data.
+ * @retval -6 Abnomal file responce.
+ */
+int refop_file_get_with_validation(const char *file, uint8_t *data, int64_t bufsize, int64_t *readsize)
+{
+ s_refop_file_header head = {0};
+ uint8_t *pbuf = NULL, *pmalloc = NULL;
+ uint16_t crc16value = 0;
+ ssize_t size = 0;
+ int result = -1,ret = -1;
+ int fd = -1;
+
+ fd = open(file, (O_CLOEXEC | O_RDONLY | O_NOFOLLOW));
+ if (fd < 0) {
+ if (errno == ENOENT)
+ return -1;
+ else
+ return -6;
+ }
+
+ size = safe_read(fd, &head, sizeof(head));
+ if (size != sizeof(head)) {
+ ret = -2;
+ goto invalid;
+ }
+
+ result = refop_header_validation(&head);
+ if (result != 0) {
+ ret = -3;
+ goto invalid;
+ }
+
+ if (head.size > bufsize) {
+ if (head.size <= refop_get_config_data_size_limit()) {
+ pmalloc = (uint8_t*)malloc(head.size);
+ pbuf = pmalloc;
+ } else {
+ ret = -4;
+ goto invalid;
+ }
+ } else {
+ pbuf = data;
+ }
+
+ size = safe_read(fd, pbuf, (size_t)head.size);
+ if (size != head.size) {
+ ret = -2;
+ goto invalid;
+ }
+
+ crc16value = crc16(0xffff, pbuf, head.size);
+ if (head.crc16 != crc16value) {
+ ret = -5;
+ goto invalid;
+ }
+
+ if (pmalloc != NULL) {
+ memcpy(data, pmalloc, bufsize);
+ free(pmalloc);
+ pmalloc = NULL;
+ (*readsize) = bufsize;
+ } else
+ (*readsize) = head.size;
+
+ (void)close(fd);
+
+ return 0;
+
+invalid:
+ free(pmalloc); //free is NULL safe
+
+ if (fd != -1)
+ (void)close(fd);
+
+ return ret;
+}
+
+/**
+ * The refop header create
+ *
+ * @param [in] head The memory of file header.
+ * @param [in] crc16value The crc value of data block.
+ * @param [in] sizevalue The size of data block.
+ */
+void refop_header_create(s_refop_file_header *head, uint16_t crc16value, uint64_t sizevalue)
+{
+ head->magic = REFOP_FILE_HEADER_MAGIC;
+
+ head->version = REFOP_FILE_HEADER_VERSION_V1;
+ head->version_inv = ~head->version;
+
+ head->crc16 = crc16value;
+ head->crc16_inv = ~head->crc16;
+
+ head->size = sizevalue;
+ head->size_inv = ~head->size;
+}
+
+/**
+ * The refop header validation
+ *
+ * @param [in] head The memory of file header.
+ *
+ * @return int
+ * @retval 0 succeeded.
+ * @retval -1 Invalid header.
+ */
+int refop_header_validation(const s_refop_file_header *head)
+{
+ int ret = -1;
+
+ //magic check
+ if (head->magic != (uint32_t)REFOP_FILE_HEADER_MAGIC)
+ goto invalid;
+
+ if (head->version == (uint32_t)(~head->version_inv)) {
+ if (head->version != REFOP_FILE_HEADER_VERSION_V1)
+ goto invalid;
+ } else
+ goto invalid;
+
+ if (head->crc16 != (uint16_t)(~head->crc16_inv))
+ goto invalid;
+
+ if (head->size != (uint64_t)(~head->size_inv))
+ goto invalid;
+
+ ret = 0;
+
+invalid:
+ return ret;
+}