是否有 *BSD 相当于 MacOS renameatx_np,原子交换文件重命名?

是否有 *BSD 相当于 MacOS renameatx_np,原子交换文件重命名?

我正在寻找创建一个 *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;
    }
}

相关内容