Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@ case "$os" in
plat_files="$plat_files ../src/compat/dummy.c"
plat_files="$plat_files ../src/compat/clearenv.c "
plat_cflags="$plat_cflags -include ../src/compat/macosx.h"
# Fuse-T (kext-free FUSE) uses NFS internally, which stats every
# directory entry after readdir. Compile in a runtime check for
# this behavior that activates a workaround.
if [ "$server" = "fuse" ]; then
plat_cflags="$plat_cflags -DFUSE_NFS_WORKAROUND"
fi
default_cc=clang
;;
FreeBSD)
Expand Down
39 changes: 29 additions & 10 deletions src/tup/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include "config.h"
#include "entry.h"
#include "option.h"
#include "pel_group.h"
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
Expand Down Expand Up @@ -62,6 +63,8 @@ int init_file_info(struct file_info *info, int do_unlink)
tent_tree_init(&info->used_groups_root);
tent_tree_init(&info->output_root);
tent_tree_init(&info->exclusion_root);
RB_INIT(&info->readdir_sticky);
RB_INIT(&info->open_readdir_sticky);
pthread_mutex_init(&info->lock, NULL);
pthread_cond_init(&info->cond, NULL);
info->server_fail = 0;
Expand All @@ -72,6 +75,8 @@ int init_file_info(struct file_info *info, int do_unlink)

void cleanup_file_info(struct file_info *info)
{
free_string_tree(&info->open_readdir_sticky);
free_string_tree(&info->readdir_sticky);
free_tent_tree(&info->exclusion_root);
free_tent_tree(&info->output_root);
free_tent_tree(&info->used_groups_root);
Expand Down Expand Up @@ -816,18 +821,32 @@ static int update_write_info(FILE *f, tupid_t cmdid, struct file_info *info,

map = TAILQ_FIRST(&info->mapping_list);

/* TODO: strcmp only here for win32 support */
if(strcmp(map->tmpname, map->realname) != 0) {
if(renameat(tup_top_fd(), map->tmpname, tup_top_fd(), map->realname) < 0) {
perror(map->realname);
fprintf(f, "tup error: Unable to rename temporary file '%s' to destination '%s'\n", map->tmpname, map->realname);
if(is_appledouble(map->realname)) {
/* macOS NFS / Fuse-T creates "._<name>" AppleDouble
* sidecars next to any file with xattrs. They reach
* us via the FUSE create path, so a tmpfile was
* already opened — discard it instead of putting an
* unwanted artifact in the source tree.
*/
if(unlinkat(tup_top_fd(), map->tmpname, 0) < 0 && errno != ENOENT) {
perror(map->tmpname);
fprintf(f, "tup error: Unable to remove AppleDouble tmp file '%s'\n", map->tmpname);
write_bork = 1;
}
}
if(map->tent) {
/* tent may not be set (in the case of hidden files) */
if(file_set_mtime(map->tent, map->realname) < 0)
return -1;
} else {
/* TODO: strcmp only here for win32 support */
if(strcmp(map->tmpname, map->realname) != 0) {
if(renameat(tup_top_fd(), map->tmpname, tup_top_fd(), map->realname) < 0) {
perror(map->realname);
fprintf(f, "tup error: Unable to rename temporary file '%s' to destination '%s'\n", map->tmpname, map->realname);
write_bork = 1;
}
}
if(map->tent) {
/* tent may not be set (in the case of hidden files) */
if(file_set_mtime(map->tent, map->realname) < 0)
return -1;
}
}
del_map(&info->mapping_list, map);
}
Expand Down
3 changes: 3 additions & 0 deletions src/tup/file.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include "tupid_tree.h"
#include "tent_tree.h"
#include "thread_tree.h"
#include "string_tree.h"
#include "pel_group.h"
#include "entry.h"
#include <stdio.h>
Expand Down Expand Up @@ -70,6 +71,8 @@ struct file_info {
struct tent_entries used_groups_root;
struct tent_entries output_root;
struct tent_entries exclusion_root;
struct string_entries readdir_sticky;
struct string_entries open_readdir_sticky;
int server_fail;
int open_count;
int do_unlink;
Expand Down
19 changes: 19 additions & 0 deletions src/tup/pel_group.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,25 @@

static _Thread_local struct mempool pool = MEMPOOL_INITIALIZER(struct path_element);

int is_appledouble(const char *path)
{
/* macOS writes AppleDouble sidecar files ("._<name>") next to any
* file with extended attributes when the underlying filesystem
* doesn't store xattrs natively — notably Fuse-T's NFS-backed
* mount used by tup itself. They are an OS-level side effect, not
* a build output. Accepts both leading-slash FUSE paths and
* relative paths (e.g. peeled `realname`s).
*/
if(path[0] == '.' && path[1] == '_')
return 1;
while((path = strchr(path, '/')) != NULL) {
if(path[1] == '.' && path[2] == '_')
return 1;
path++;
}
return 0;
}

int pel_ignored(const char *path, int len)
{
if(len < 0)
Expand Down
1 change: 1 addition & 0 deletions src/tup/pel_group.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ struct pel_group {

void init_pel_group(struct pel_group *pg);
int pel_ignored(const char *path, int len);
int is_appledouble(const char *path);
int get_path_elements(const char *dir, struct pel_group *pg);
void free_pel(struct path_element *pel);
void del_pel(struct path_element *pel, struct pel_group *pg);
Expand Down
6 changes: 6 additions & 0 deletions src/tup/server/Tupfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@ include_rules
ifeq ($(TUP_SERVER),fuse)
export PKG_CONFIG_PATH
CFLAGS += `pkg-config fuse --cflags`
ifeq (@(TUP_PLATFORM),macosx)
# Fuse-T (kext-free FUSE) uses NFS internally, which stats every
# directory entry after readdir. Compile in a runtime check for
# this behavior that activates a workaround.
CFLAGS += -DFUSE_NFS_WORKAROUND
endif
: foreach fuse_server.c fuse_fs.c master_fork.c symlink.c |> !cc |>
endif

Expand Down
Loading