Skip to content

Commit 1347c5c

Browse files
committed
feat: add func to write encrypt file header.
1 parent 4963437 commit 1347c5c

File tree

2 files changed

+271
-0
lines changed

2 files changed

+271
-0
lines changed

include/os/osFile.h

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,74 @@ bool lastErrorIsFileNotExist();
146146

147147
int64_t taosWritevFile(TdFilePtr pFile, const TaosIOVec *iov, int iovcnt);
148148

149+
// ============================================================================
150+
// Encrypted File Operations
151+
// ============================================================================
152+
153+
// File encryption magic number and constants
154+
#define TD_ENCRYPT_FILE_MAGIC "tdEncrypt"
155+
#define TD_ENCRYPT_FILE_VERSION 1
156+
#define TD_ENCRYPT_MAGIC_LEN 16
157+
158+
// Database encryption status
159+
typedef enum {
160+
TD_DB_ENCRYPT_STATUS_UNKNOWN = 0, // Unknown - encryption state uncertain (upgrade scenario)
161+
TD_DB_ENCRYPT_STATUS_PLAIN = 1, // Plain - database files are unencrypted
162+
TD_DB_ENCRYPT_STATUS_ENCRYPTED = 2 // Encrypted - database files are fully encrypted
163+
} ETdDbEncryptStatus;
164+
165+
// DNode encryption status
166+
typedef enum {
167+
TD_DNODE_ENCRYPT_STATUS_PLAIN = 0, // Plain - config and metadata files are unencrypted
168+
TD_DNODE_ENCRYPT_STATUS_ENCRYPTED = 1 // Encrypted - all related files are encrypted
169+
} ETdDnodeEncryptStatus;
170+
171+
// Encrypt file header structure (plaintext part at file beginning)
172+
typedef struct {
173+
char magic[TD_ENCRYPT_MAGIC_LEN]; // Magic number "tdEncrypt"
174+
int32_t algorithm; // Encryption algorithm (e.g., TSDB_ENCRYPT_ALGO_SM4 = 1)
175+
int32_t version; // File format version
176+
int32_t dataLen; // Length of encrypted data following header
177+
char reserved[32]; // Reserved for future use
178+
} STdEncryptFileHeader;
179+
180+
/**
181+
* @brief Write file with encryption header using atomic file replacement
182+
*
183+
* This function writes data to a file with an encryption header at the beginning.
184+
* The caller is responsible for encrypting the data before passing it to this function.
185+
* It uses atomic file replacement strategy: writes to temp file, then renames.
186+
*
187+
* @param filepath Target file path
188+
* @param algorithm Encryption algorithm identifier (e.g., TSDB_ENCRYPT_ALGO_SM4)
189+
* @param data Data buffer to write (caller should encrypt data if needed, can be NULL for empty file)
190+
* @param dataLen Length of data to write (0 for empty file)
191+
* @return 0 on success, error code on failure
192+
*/
193+
int32_t taosWriteEncryptFileHeader(const char *filepath, int32_t algorithm, const void *data, int32_t dataLen);
194+
195+
/**
196+
* @brief Read encryption header from file
197+
*
198+
* Reads and validates the encryption header from the beginning of a file.
199+
*
200+
* @param filepath File path to read
201+
* @param header Output parameter for header data
202+
* @return 0 on success, error code on failure
203+
*/
204+
int32_t taosReadEncryptFileHeader(const char *filepath, STdEncryptFileHeader *header);
205+
206+
/**
207+
* @brief Check if file has encryption header
208+
*
209+
* Quickly checks if a file begins with the encryption magic number.
210+
*
211+
* @param filepath File path to check
212+
* @param algorithm Output parameter for algorithm (can be NULL)
213+
* @return true if file is encrypted, false otherwise
214+
*/
215+
bool taosIsEncryptedFile(const char *filepath, int32_t *algorithm);
216+
149217
#ifdef BUILD_WITH_RAND_ERR
150218
#define STUB_RAND_NETWORK_ERR(ret) \
151219
do { \

source/os/src/osFile.c

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1855,3 +1855,206 @@ int64_t taosWritevFile(TdFilePtr pFile, const TaosIOVec *iov, int iovcnt) {
18551855
return (int64_t)totalWritten;
18561856
#endif
18571857
}
1858+
1859+
// ============================================================================
1860+
// Encrypted File Operations Implementation
1861+
// ============================================================================
1862+
1863+
/**
1864+
* Write file with encryption header using atomic file replacement.
1865+
*
1866+
* This function writes data to a file with an encryption header at the beginning.
1867+
* The encryption header contains:
1868+
* - Magic number "tdEncrypt" for quick identification
1869+
* - Algorithm identifier (e.g., SM4 = 1)
1870+
* - File format version
1871+
* - Length of encrypted data
1872+
*
1873+
* Atomic file replacement strategy:
1874+
* 1. Write to temporary file: filepath.tmp.timestamp
1875+
* 2. Sync temporary file to disk
1876+
* 3. Atomically rename temporary file to target filepath
1877+
* 4. Remove old file if rename succeeds
1878+
*
1879+
* This ensures the operation is atomic - no partial writes or corrupted files
1880+
* even if the process is interrupted.
1881+
*
1882+
* @param filepath Target file path
1883+
* @param algorithm Encryption algorithm identifier
1884+
* @param data Data buffer to write (can be NULL for empty file with header only)
1885+
* @param dataLen Length of data to write (0 for empty file)
1886+
* @return 0 on success, error code on failure
1887+
*/
1888+
int32_t taosWriteEncryptFileHeader(const char *filepath, int32_t algorithm, const void *data, int32_t dataLen) {
1889+
if (filepath == NULL) {
1890+
terrno = TSDB_CODE_INVALID_PARA;
1891+
return terrno;
1892+
}
1893+
1894+
// Validate algorithm
1895+
if (algorithm < 0) {
1896+
terrno = TSDB_CODE_INVALID_PARA;
1897+
return terrno;
1898+
}
1899+
1900+
// Validate data parameters
1901+
if (dataLen > 0 && data == NULL) {
1902+
terrno = TSDB_CODE_INVALID_PARA;
1903+
return terrno;
1904+
}
1905+
1906+
int32_t code = 0;
1907+
int64_t now = taosGetTimestampMs();
1908+
1909+
// Prepare encryption header (plaintext)
1910+
STdEncryptFileHeader header;
1911+
memset(&header, 0, sizeof(STdEncryptFileHeader));
1912+
strncpy(header.magic, TD_ENCRYPT_FILE_MAGIC, TD_ENCRYPT_MAGIC_LEN - 1);
1913+
header.algorithm = algorithm;
1914+
header.version = TD_ENCRYPT_FILE_VERSION;
1915+
header.dataLen = dataLen;
1916+
1917+
// Create temporary file for atomic write
1918+
char tempFile[PATH_MAX];
1919+
snprintf(tempFile, sizeof(tempFile), "%s.tmp.%ld", filepath, now);
1920+
1921+
// Open temp file
1922+
TdFilePtr pFile = taosOpenFile(tempFile, TD_FILE_CREATE | TD_FILE_WRITE | TD_FILE_TRUNC);
1923+
if (pFile == NULL) {
1924+
code = terrno;
1925+
return code;
1926+
}
1927+
1928+
// Write header (plaintext)
1929+
int64_t written = taosWriteFile(pFile, &header, sizeof(STdEncryptFileHeader));
1930+
if (written != sizeof(STdEncryptFileHeader)) {
1931+
code = (terrno != 0) ? terrno : TSDB_CODE_FILE_CORRUPTED;
1932+
taosCloseFile(&pFile);
1933+
taosRemoveFile(tempFile);
1934+
terrno = code;
1935+
return code;
1936+
}
1937+
1938+
// Write data if present
1939+
if (dataLen > 0 && data != NULL) {
1940+
written = taosWriteFile(pFile, data, dataLen);
1941+
if (written != dataLen) {
1942+
code = (terrno != 0) ? terrno : TSDB_CODE_FILE_CORRUPTED;
1943+
taosCloseFile(&pFile);
1944+
taosRemoveFile(tempFile);
1945+
terrno = code;
1946+
return code;
1947+
}
1948+
}
1949+
1950+
// Sync to disk
1951+
code = taosFsyncFile(pFile);
1952+
if (code != 0) {
1953+
taosCloseFile(&pFile);
1954+
taosRemoveFile(tempFile);
1955+
return code;
1956+
}
1957+
1958+
// Close temp file
1959+
taosCloseFile(&pFile);
1960+
1961+
#ifndef WINDOWS
1962+
// Set file permissions (600 - owner read/write only) for security
1963+
chmod(tempFile, 0600);
1964+
#endif
1965+
1966+
// Atomic replacement - rename temp file to target
1967+
code = taosRenameFile(tempFile, filepath);
1968+
if (code != 0) {
1969+
taosRemoveFile(tempFile);
1970+
return code;
1971+
}
1972+
1973+
return 0;
1974+
}
1975+
1976+
/**
1977+
* Read encryption header from file.
1978+
*
1979+
* Reads and validates the encryption header from the beginning of a file.
1980+
* Checks:
1981+
* - Magic number matches "tdEncrypt"
1982+
* - Version is supported
1983+
*
1984+
* @param filepath File path to read
1985+
* @param header Output parameter for header data
1986+
* @return 0 on success, error code on failure
1987+
*/
1988+
int32_t taosReadEncryptFileHeader(const char *filepath, STdEncryptFileHeader *header) {
1989+
if (filepath == NULL || header == NULL) {
1990+
terrno = TSDB_CODE_INVALID_PARA;
1991+
return terrno;
1992+
}
1993+
1994+
// Open file for reading
1995+
TdFilePtr pFile = taosOpenFile(filepath, TD_FILE_READ);
1996+
if (pFile == NULL) {
1997+
return terrno;
1998+
}
1999+
2000+
// Read header
2001+
int64_t nread = taosReadFile(pFile, header, sizeof(STdEncryptFileHeader));
2002+
taosCloseFile(&pFile);
2003+
2004+
if (nread != sizeof(STdEncryptFileHeader)) {
2005+
terrno = TSDB_CODE_FILE_CORRUPTED;
2006+
return terrno;
2007+
}
2008+
2009+
// Verify magic number
2010+
if (strncmp(header->magic, TD_ENCRYPT_FILE_MAGIC, strlen(TD_ENCRYPT_FILE_MAGIC)) != 0) {
2011+
terrno = TSDB_CODE_FILE_CORRUPTED;
2012+
return terrno;
2013+
}
2014+
2015+
// Verify version (currently only version 1 is supported)
2016+
if (header->version != TD_ENCRYPT_FILE_VERSION) {
2017+
terrno = TSDB_CODE_FILE_CORRUPTED;
2018+
return terrno;
2019+
}
2020+
2021+
return 0;
2022+
}
2023+
2024+
/**
2025+
* Check if file has encryption header.
2026+
*
2027+
* Quickly checks if a file begins with the encryption magic number.
2028+
* This is faster than reading the full header when you only need to
2029+
* know if the file is encrypted.
2030+
*
2031+
* @param filepath File path to check
2032+
* @param algorithm Output parameter for algorithm (can be NULL)
2033+
* @return true if file is encrypted, false otherwise
2034+
*/
2035+
bool taosIsEncryptedFile(const char *filepath, int32_t *algorithm) {
2036+
if (filepath == NULL) {
2037+
terrno = TSDB_CODE_INVALID_PARA;
2038+
return false;
2039+
}
2040+
2041+
// Check if file exists
2042+
if (!taosCheckExistFile(filepath)) {
2043+
return false;
2044+
}
2045+
2046+
// Read header
2047+
STdEncryptFileHeader header;
2048+
int32_t code = taosReadEncryptFileHeader(filepath, &header);
2049+
2050+
if (code != 0) {
2051+
return false;
2052+
}
2053+
2054+
// Return algorithm if requested
2055+
if (algorithm != NULL) {
2056+
*algorithm = header.algorithm;
2057+
}
2058+
2059+
return true;
2060+
}

0 commit comments

Comments
 (0)