我正在寻找创建一个 *BSD 兼容的 C 函数,例如下面那个:
int
fs_ext__swap (const char *from, const char *to) {
int res = renameatx_np(AT_FDCWD, from, AT_FDCWD, to, RENAME_SWAP);
return res == -1 ? uv_translate_sys_error(errno) : res;
}
目前我收到构建错误 ( 'renameatx_np' is invalid in C99 [-Wimplicit-function-declaration]
),因为据我所知,renameatx_np
功能是 MacOS 所独有的。 *BSD 是否有等效版本(我使用的是 OpenBSD 7.3 atm)?
RENAME_SWAP
如果我删除该标志并将函数更改为,我会冒什么风险renameat
(编辑:我尝试了这个,它构建和编译正常,但在上游测试期间导致 ENOENT 错误)?
我的计划是使用林卡特和取消链接函数来模拟renameatx_np的功能。
我还能如何处理这个问题?
答案1
对标题问题的简短回答是不,据我所知。
然而,有足够的本机函数可用于实现 fs_ext__swap 函数。就是这样我做到了:
#include <fcntl.h>
#include <stdint.h>
#include <uv.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <libgen.h>
#include "../include/fs-ext.h"
#include "platform.h"
void random_string_len10(char *str){
// Creates a 10 char long random alpha-numeric string (with in the input char array). Note the input array *str must have length of 11 to allow for 10 chars and a '\0' termination.
const char charset[] = "0123456789abcdefghijklmnopqrstuvwxyz";
size_t charset_length = strlen(charset);
size_t str_length = 10;
for (size_t i = 0; i < str_length; ++i){
str[i] = charset[arc4random_uniform(charset_length -1)];
}
str[str_length] = '\0';
}
int append_file_to_path_string(char *path, char *filename, char *result)
{
// function to append a filename to directory name. Directory name must have a slash at the end.
size_t path_length = strlen(path);
size_t filename_length = strlen(filename);
size_t result_size = path_length + filename_length + 1; // +1 for null terminator
if (result_size > PATH_MAX ) {
fprintf(stderr, "Cannot append file name to to path; result size is too large!\n");
return EXIT_FAILURE;
}
strncpy(result, path, path_length);
strncpy(result + path_length, filename, filename_length);
result[result_size - 1] = '\0'; // Ensure null termination
return EXIT_SUCCESS;
}
void append_slash(char *str) {
// appends a slash to an input string
size_t len = strlen(str);
if (len > 0 && str[len -1] != '/' && len < PATH_MAX-1) {
str[len] = '/';
str[len+1] = '\0';
}
}
int
swap_directories (const char *from, const char *to) {
// *****************
// Prep temporary directory with same underlying path as from
char temp_dir[PATH_MAX];
snprintf(temp_dir,sizeof(temp_dir), "%s.swap_temp", from);
// *****************
// Perform series of rename operations to swap to and from directories.
// 1. Rename fromdir to tempdir
if( renameat( AT_FDCWD, from, AT_FDCWD, temp_dir) == -1) {
printf("Renameat from - temp failed.\n");
return uv_translate_sys_error(errno);
} else {
printf("Renameat from - to worked!\n");
}
// 2. Rename todir to fromdir
if( renameat( AT_FDCWD, to, AT_FDCWD, from) == -1) {
printf("Renameat to - from failed.\n");
return uv_translate_sys_error(errno);
} else {
printf("Renameat to - from worked!\n");
}
// 3. Rename temp_dir(now original fromdir) to todir
if( renameat( AT_FDCWD, temp_dir, AT_FDCWD, to) == -1) {
printf("Renameat temp - to failed.\n");
if( renameat( AT_FDCWD, from, AT_FDCWD, to) == -1) { printf("Rollback failed.\n");}
return uv_translate_sys_error(errno);
} else {
printf("Renameat temp - to worked!\n");
}
return 0;
}
int
swap_files (const char *from, const char *to) {
// *****************
// Prep temporary files with random names. Must share path of input files to avoid cross file system error.
char temp_nameA[11];
char temp_nameB[11];
random_string_len10(temp_nameA);
random_string_len10(temp_nameB);
char *to_path = dirname(strdup(to));
size_t pathlen = strlen(to_path);
if ( pathlen + 1 < PATH_MAX){
append_slash(to_path);
} else {
return -1;
}
char temp_fileA[PATH_MAX];
char temp_fileB[PATH_MAX];
append_file_to_path_string(to_path,temp_nameA,temp_fileA);
append_file_to_path_string(to_path,temp_nameB,temp_fileB);
// *****************
// Perform series of linking and unlinking operations to swap to and from file. "TO-file" and "FROM-file" in the comments denote the original underlying file objects.
// 1. LINK (from,tempA) tempA and from can access FROM-file
if( linkat( AT_FDCWD, from, AT_FDCWD, temp_fileA,0)== -1) {
printf("link(from tempA) failed.\n");
return uv_translate_sys_error(errno);
}
else {
printf("link(from tempA) worked!\n");
}
// 2. LINK (to,tempB) tempB and to can access TO-file
if( linkat( AT_FDCWD, to, AT_FDCWD, temp_fileB,0)== -1) {
printf("link(to tempB) failed.\n");
return uv_translate_sys_error(errno);
} else {
printf("link(to tempB) worked!\n");
}
// 3. UNLINK (from) only tempA can access FROM-file
if( unlinkat( AT_FDCWD, from,0)== -1) {
printf("unlink(from) failed.\n");
return uv_translate_sys_error(errno);
} else {
printf("unlink(from) worked!\n");
}
// 4. UNLINK (to) only tempB can access TO-file
if( unlinkat( AT_FDCWD, to,0)== -1) {
printf("unlink(to) failed.\n");
return uv_translate_sys_error(errno);
} else {
printf("unlink(to) worked!\n");
}
// 5. LINK (tempA,to) tempA and to can access FROM-file
if( linkat( AT_FDCWD, temp_fileA, AT_FDCWD, to,0)== -1) {
printf("link(tempB to) failed.\n");
return uv_translate_sys_error(errno);
} else {
printf("link(to tempB) worked!\n");
}
// 6. LINK (tempB,from) tempB and from can access TO-file
if( linkat( AT_FDCWD, temp_fileB, AT_FDCWD, from,0)== -1) {
printf("link(tempB from) failed.\n");
return uv_translate_sys_error(errno);
} else {
printf("link(tempB,from) worked!\n");
}
// 7. UNLINK (tempA) only to can access FROM-file
if( unlinkat( AT_FDCWD, temp_fileA,0)== -1) {
printf("unlink(tempA) failed.\n");
return uv_translate_sys_error(errno);
} else {
printf("unlink(tempA) worked!\n");
}
// 8. UNLINK (tempB) only from can access TO-file
if( unlinkat( AT_FDCWD, temp_fileB,0)== -1) {
printf("unlink(tempB) failed.\n");
return uv_translate_sys_error(errno);
} else {
printf("unlink(tempB) worked!\n");
}
return 0;
}
int
fs_ext__swap (const char *from, const char *to) {
// use sys/stat.h to determine if from and to are files or directories
struct stat st_from, st_to;
int from_is_dir = stat(from, &st_from) == 0 && S_ISDIR(st_from.st_mode);
int to_is_dir = stat(to, &st_to) == 0 && S_ISDIR(st_to.st_mode);
// Call swap_files or swap_directories dependendent whether inputs are files or directories:
switch (from_is_dir * 2 + to_is_dir) {
case 0: // Both are files
if( swap_files(from,to)!=0){ return uv_translate_sys_error(errno);} // swap files
return 0;
case 1: // from is file to is dir: file dir-swap seems to work with case 3 code, so no return statements included.
case 2: // from is dir to is file
case 3: // Both are dirs
if (swap_directories(from,to)!=0){ return uv_translate_sys_error(errno);}
return 0;
default: // something else: ERR
return -1;
}
}