私は次の* 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;
}
}