Skip to content

Commit ae77d3e

Browse files
lneelyLevi Neelyclaude
authored
Add testability infrastructure: unit tests, extracted modules, CI integration (#381)
* Add unit test for psync_task_free refcount fix (#377) Adds tests/unit-tests/test_ptask_free.c to verify all code paths of the psync_task_free fix from #377: single-owner free, last-ref destroy, non-last-ref decrement, READY task signaling, and lock-before-refcnt ordering. All 5 tests pass. Also adds compiled binary to .gitignore. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Refactor test_ptask_free to link production code via --wrap Extract psync_task_free + psync_task_destroy (and their static helpers psync_task_dec_refcnt, psync_task_entry) from ptask.c into a new separately-compilable unit pclsync/ptask_free.c. Add pclsync/ptask_free_internal.h to expose the internal struct layout (struct psync_task_manager_t_ / struct psync_task_t_) for test use without pulling in ptask.c's heavyweight transitive dependencies. Rewrite tests/unit-tests/test_ptask_free.c to: - Include ptask_free_internal.h instead of duplicating structs inline - Call the real psync_task_free() rather than a local replica - Intercept pthread_mutex_lock/unlock and pmem_free via --wrap linker flags to observe lock discipline and detect destroy invocations Update the Makefile test_ptask_free target to link pclsync/ptask_free.c and pass the required --wrap flags. Production build unchanged: ptask_free.o is picked up automatically by the existing wildcard COBJ rule. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Implement Tasks #3, #4, #5: tree tests, pfstasks tree layer, DB harness Task #3 — Unit tests for ptree and pintervaltree tests/unit-tests/test_ptree.c: 8 tests covering single-node insert, in-order traversal after arbitrary and reverse inserts, BST lookup, leaf/root/all-node deletion, and ptree_for_each visitation. tests/unit-tests/test_pintervaltree.c: 18 tests covering single add, non-overlapping, overlapping/adjacent/contained/spanning merges, chain merge, remove middle split, remove exact/left/right/spanning, cut_end, first_interval_containing_or_after, and free(NULL). Task #4 — Extract pfstasks tree layer pclsync/pfstasks_tree.h + pclsync/pfstasks_tree.c: pure tree layer (zero psql calls) extracted from pfstasks.c — pfs_task_search_tree, pfs_task_walk_tree (static helpers), pfs_task_insert_into_tree, pfs_task_find_mkdir/rmdir/creat/unlink, pfs_task_find_mkdir_by_folderid, pfs_task_find_creat_by_fileid. pclsync/pfstasks.c: #includes pfstasks_tree.h; all moved functions removed; all callers unchanged. tests/unit-tests/test_pfstasks_tree.c: 13 tests using direct tree construction (no DB) to verify find-by-name, taskid discrimination, find-by-numeric-id, and empty-folder edge cases. Task #5 — psql in-memory harness + pfstasks DB tests tests/helpers/psql_test_helpers.h + .c: lightweight harness that opens :memory: via sqlite3_open, enables PRAGMA foreign_keys=ON, and applies the full PSYNC_DATABASE_STRUCTURE schema. Exposes psql_test_db(), psql_test_exec(), psql_test_insert_fstask(), psql_test_count_fstask/fstaskdepend(). No dependency on psql.c. tests/unit-tests/test_pfstasks_db.c: 10 tests verifying schema creation, fstask insertion/query, fstaskdepend insertion, CASCADE DELETE propagation, FK enforcement, rmdir-blocking SQL pattern, creat-after-unlink sequencing, and open/close idempotence. All 11 new tests pass; production build clean. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Fix P1 review findings in pfstasks_db test and helpers 1. Check psql_test_exec() return values in test_cascade_delete() and test_creat_after_unlink() consistently with test_fstaskdepend_insert(). 2. Remove dead dep_cnt variable and (void)dep_cnt suppressor from test_creat_after_unlink(). 3. Change SQLITE_STATIC → SQLITE_TRANSIENT for text1 binding in psql_test_insert_fstask() to avoid dangling-pointer footgun on future reuse. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Fix ASAN/LSAN failures: ppath_home stack-use-after-scope + intentional leak pclsync/ppath.c: Move buff[4096] to function scope in ppath_home() so the pointer stored in dir via result->pw_dir remains live through the putil_strdup(dir) call. Previously buff went out of scope at the if-block close, causing a stack-use-after-scope ASAN report on every call that fell through the getpwuid_r path. tests/unit-tests/test_ptools_errptr.c: run_unfixed() intentionally leaks errPtr to demonstrate the pre-fix bug. Wrap the allocation with LSAN_DISABLE() / LSAN_ENABLE() so LSAN does not abort the process at exit before stdio flushes, which was causing a non-zero exit code. The guard uses nested #ifdef/__has_feature to remain compatible with both GCC (__SANITIZE_ADDRESS__) and Clang (__has_feature(address_sanitizer)) without triggering "missing binary operator" errors on GCC. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Implement Tasks #11 and #12: plocks stress test + pfsupload send tests Task #11 — plocks.c stress test (test_plocks.c) 7 tests: basic rdlock/wrlock round-trip, recursive TLS counting (same thread acquires rdlock N times; unlock only releases on final decrement), upgrade under contention (N readers + towrlock; barrier-synchronized), writer starvation prevention (sustained reader load; writer acquires within 500ms), N-reader + M-writer counter-integrity stress test (ASAN), and try-variant contention (trywrlock fails when another thread holds rdlock). TSAN note documented: custom lock internals require ASAN-only when ThreadSanitizer annotations are absent. Task #12 — pfsupload send-function tests (pfsupload_send.c/h + test_pfsupload.c) Extract psync_send_task_mkdir and psync_send_task_rmdir from pfsupload.c into pclsync/pfsupload_send.c as non-static pfsupload_send_mkdir/rmdir. Expose fsupload_task_t struct via pclsync/pfsupload_send.h. Add __attribute__((weak)) get_urls() as an injectable URL seam for large- upload paths. pfsupload.c updated to include pfsupload_send.h and use the renamed functions in its dispatch table; pfsupload_send.o is automatically picked up by the production wildcard build. 5 tests: mkdir (non-encrypted) command + folderid param, mkdir (encrypted) key param present, rmdir command + sfolderid, API error path (papi_send failure → -1), get_urls() weak override. Uses --wrap=papi_send to intercept API calls and socketpair() to provide a valid psock_t without real network I/O. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * P2 cleanup: comments, make_fake_api stack alloc, find_str_param fix 1. test_plocks.c: add comment to test_upgrade_under_contention clarifying it verifies towrlock completion and holding_wrlock; notes that concurrent exclusivity is covered by test_stress(). 2. test_pfsupload.c / make_fake_api: replace static-local psock_t with caller-supplied stack allocation (out parameter) to eliminate the multiple-calls-per-test footgun. 3. test_pfsupload.c / find_str_param: replace ternary `paramnamelen == strlen ? paramname : ""` with explicit length check + strncmp, matching the cleaner pattern used in find_num_param. 4. Makefile: add comment next to -Wl,--wrap=papi_send noting it redirects papi_send to __wrap_papi_send and is GNU ld only (not macOS Apple ld). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Levi Neely <lkn@darkstar.example.net> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 79a4a56 commit ae77d3e

22 files changed

Lines changed: 2676 additions & 476 deletions

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,5 +32,4 @@ tests/test_prun
3232
tests/test_ptools_errptr
3333
tests/test_ptools_params
3434
tests/test_read_response
35-
tests/test_ptask_free
3635
tests/test_*

Makefile

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,8 @@ TESTS_DIR := tests
155155
TEST_CFLAGS := -D_POSIX_C_SOURCE=200809L
156156
TEST_CXXFLAGS := -D_POSIX_C_SOURCE=200809L
157157

158+
HELPERS_DIR := tests/helpers
159+
158160
TEST_BINS := \
159161
tests/test_pdbg_path \
160162
tests/test_ptools_params \
@@ -163,7 +165,13 @@ TEST_BINS := \
163165
tests/test_prun \
164166
tests/test_ptools_errptr \
165167
tests/test_read_response \
166-
tests/test_signal_safety
168+
tests/test_signal_safety \
169+
tests/test_ptree \
170+
tests/test_pintervaltree \
171+
tests/test_pfstasks_tree \
172+
tests/test_pfstasks_db \
173+
tests/test_plocks \
174+
tests/test_pfsupload
167175

168176
.PHONY: test tests check clean-tests
169177

@@ -191,8 +199,12 @@ tests/test_ptools_params: $(UNIT_DIR)/test_ptools_params.c $(LIBDIR)/ptools.c $(
191199
tests/test_pfs_lock_ordering: $(UNIT_DIR)/test_pfs_lock_ordering.c
192200
$(CC) $(TEST_CFLAGS) $(CFLAGS) -o $@ $< -lpthread
193201

194-
tests/test_ptask_free: $(UNIT_DIR)/test_ptask_free.c
195-
$(CC) $(TEST_CFLAGS) $(CFLAGS) -o $@ $< -lpthread
202+
tests/test_ptask_free: $(UNIT_DIR)/test_ptask_free.c $(LIBDIR)/ptask_free.c
203+
$(CC) $(TEST_CFLAGS) $(CFLAGS) -o $@ $^ \
204+
-Wl,--wrap=pthread_mutex_lock \
205+
-Wl,--wrap=pthread_mutex_unlock \
206+
-Wl,--wrap=pmem_free \
207+
-lpthread
196208

197209
tests/test_prun: $(UNIT_DIR)/test_prun.c $(LIBDIR)/prun.c $(LIBDIR)/pdbg.c $(LIBDIR)/pmem.c $(LIBDIR)/putil.c $(LIBDIR)/ppath.c tests/stubs/test_stubs.c
198210
$(CC) -D_POSIX_C_SOURCE=199309L $(CFLAGS) -o $@ $^ \
@@ -207,6 +219,25 @@ tests/test_ptools_errptr: $(UNIT_DIR)/test_ptools_errptr.c $(LIBDIR)/ptools.c $(
207219
-Wl,--wrap=malloc \
208220
-Wl,--wrap=free
209221

222+
tests/test_ptree: $(UNIT_DIR)/test_ptree.c $(LIBDIR)/ptree.c $(LIBDIR)/pdbg.c $(LIBDIR)/pmem.c $(LIBDIR)/putil.c $(LIBDIR)/ppath.c tests/stubs/test_stubs.c
223+
$(CC) $(TEST_CFLAGS) $(CFLAGS) -o $@ $^
224+
225+
tests/test_pintervaltree: $(UNIT_DIR)/test_pintervaltree.c $(LIBDIR)/pintervaltree.c $(LIBDIR)/ptree.c $(LIBDIR)/pdbg.c $(LIBDIR)/pmem.c $(LIBDIR)/putil.c $(LIBDIR)/ppath.c tests/stubs/test_stubs.c
226+
$(CC) $(TEST_CFLAGS) $(CFLAGS) -o $@ $^
227+
228+
tests/test_pfstasks_tree: $(UNIT_DIR)/test_pfstasks_tree.c $(LIBDIR)/pfstasks_tree.c $(LIBDIR)/ptree.c $(LIBDIR)/pdbg.c $(LIBDIR)/pmem.c $(LIBDIR)/putil.c $(LIBDIR)/ppath.c tests/stubs/test_stubs.c
229+
$(CC) $(TEST_CFLAGS) $(CFLAGS) -o $@ $^
230+
231+
tests/test_pfstasks_db: $(UNIT_DIR)/test_pfstasks_db.c $(HELPERS_DIR)/psql_test_helpers.c
232+
$(CC) $(TEST_CFLAGS) $(CFLAGS) -I$(HELPERS_DIR) -o $@ $^ -lsqlite3
233+
234+
tests/test_plocks: $(UNIT_DIR)/test_plocks.c $(LIBDIR)/plocks.c $(LIBDIR)/pdbg.c $(LIBDIR)/pmem.c $(LIBDIR)/putil.c $(LIBDIR)/ppath.c tests/stubs/test_stubs.c
235+
$(CC) $(TEST_CFLAGS) $(CFLAGS) -o $@ $^ -lpthread
236+
237+
tests/test_pfsupload: $(UNIT_DIR)/test_pfsupload.c $(LIBDIR)/pfsupload_send.c $(LIBDIR)/pdbg.c $(LIBDIR)/pmem.c $(LIBDIR)/putil.c $(LIBDIR)/ppath.c tests/stubs/test_stubs.c
238+
$(CC) $(TEST_CFLAGS) $(CFLAGS) -o $@ $^ \
239+
-Wl,--wrap=papi_send # redirect papi_send → __wrap_papi_send; GNU ld only (not macOS Apple ld)
240+
210241
tests/test_read_response: $(UNIT_DIR)/test_read_response.cpp rpcclient.cpp tests/stubs/test_stubs_cpp.c
211242
$(CXX) $(TEST_CXXFLAGS) $(CXXFLAGS) -o $@ $^
212243

pclsync/pfstasks.c

Lines changed: 1 addition & 144 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
#include "pfoldersync.h"
4141
#include "pfs.h"
4242
#include "pfstasks.h"
43+
#include "pfstasks_tree.h"
4344
#include "pfsupload.h"
4445
#include "plibs.h"
4546
#include "pmem.h"
@@ -192,150 +193,6 @@ void pfs_task_release_folder_tasks_locked(psync_fstask_folder_t *folder) {
192193
}
193194
}
194195

195-
static psync_tree *pfs_task_search_tree(psync_tree *tree, size_t nameoff,
196-
const char *name, uint64_t taskid,
197-
size_t taskidoff) {
198-
int c;
199-
while (tree) {
200-
c = strcmp(name, ((char *)tree) + nameoff);
201-
if (c < 0)
202-
tree = tree->left;
203-
else if (c > 0)
204-
tree = tree->right;
205-
else
206-
break;
207-
}
208-
if (!tree || !taskid || *((uint64_t *)(((char *)tree) + taskidoff)) == taskid)
209-
return tree;
210-
else {
211-
psync_tree *tn;
212-
tn = ptree_get_prev(tree);
213-
while (tn) {
214-
if (strcmp(name, ((char *)tn) + nameoff))
215-
break;
216-
if (*((uint64_t *)(((char *)tn) + taskidoff)) == taskid)
217-
return tn;
218-
tn = ptree_get_prev(tn);
219-
}
220-
tn = ptree_get_next(tree);
221-
while (tn) {
222-
if (strcmp(name, ((char *)tn) + nameoff))
223-
break;
224-
if (*((uint64_t *)(((char *)tn) + taskidoff)) == taskid)
225-
return tn;
226-
tn = ptree_get_next(tn);
227-
}
228-
return NULL;
229-
}
230-
}
231-
232-
static psync_tree *pfs_task_walk_tree(psync_tree *tree, uint64_t taskid,
233-
size_t taskidoff) {
234-
tree = ptree_get_first(tree);
235-
while (tree) {
236-
if (*((uint64_t *)(((char *)tree) + taskidoff)) == taskid)
237-
return tree;
238-
tree = ptree_get_next(tree);
239-
}
240-
return NULL;
241-
}
242-
243-
static void pfs_task_insert_into_tree(psync_tree **tree, size_t nameoff,
244-
psync_tree *element) {
245-
const char *name;
246-
psync_tree *node;
247-
int c;
248-
249-
if (!*tree) {
250-
ptree_add_after(tree, NULL, element);
251-
return;
252-
}
253-
254-
name = ((char *)element) + nameoff;
255-
node = *tree;
256-
257-
while (1) {
258-
c = strcmp(name, ((char *)node) + nameoff);
259-
260-
if (c < 0) {
261-
if (node->left)
262-
node = node->left;
263-
else {
264-
ptree_add_before(tree, node, element);
265-
return;
266-
}
267-
} else {
268-
if (c == 0)
269-
pdbg_logf(D_WARNING, "duplicate entry %s, should not happen", name);
270-
271-
if (node->right)
272-
node = node->right;
273-
else {
274-
ptree_add_after(tree, node, element);
275-
return;
276-
}
277-
}
278-
}
279-
}
280-
281-
psync_fstask_mkdir_t *pfs_task_find_mkdir(psync_fstask_folder_t *folder,
282-
const char *name,
283-
uint64_t taskid) {
284-
return ptree_element(
285-
pfs_task_search_tree(folder->mkdirs,
286-
offsetof(psync_fstask_mkdir_t, name), name,
287-
taskid, offsetof(psync_fstask_mkdir_t, taskid)),
288-
psync_fstask_mkdir_t, tree);
289-
}
290-
291-
psync_fstask_rmdir_t *pfs_task_find_rmdir(psync_fstask_folder_t *folder,
292-
const char *name,
293-
uint64_t taskid) {
294-
return ptree_element(
295-
pfs_task_search_tree(folder->rmdirs,
296-
offsetof(psync_fstask_rmdir_t, name), name,
297-
taskid, offsetof(psync_fstask_rmdir_t, taskid)),
298-
psync_fstask_rmdir_t, tree);
299-
}
300-
301-
psync_fstask_creat_t *pfs_task_find_creat(psync_fstask_folder_t *folder,
302-
const char *name,
303-
uint64_t taskid) {
304-
return ptree_element(
305-
pfs_task_search_tree(folder->creats,
306-
offsetof(psync_fstask_creat_t, name), name,
307-
taskid, offsetof(psync_fstask_creat_t, taskid)),
308-
psync_fstask_creat_t, tree);
309-
}
310-
311-
psync_fstask_unlink_t *pfs_task_find_unlink(psync_fstask_folder_t *folder,
312-
const char *name,
313-
uint64_t taskid) {
314-
return ptree_element(
315-
pfs_task_search_tree(folder->unlinks,
316-
offsetof(psync_fstask_unlink_t, name), name,
317-
taskid, offsetof(psync_fstask_unlink_t, taskid)),
318-
psync_fstask_unlink_t, tree);
319-
}
320-
321-
psync_fstask_mkdir_t *
322-
pfs_task_find_mkdir_by_folderid(psync_fstask_folder_t *folder,
323-
psync_fsfolderid_t folderid) {
324-
return ptree_element(
325-
pfs_task_walk_tree(folder->mkdirs, folderid,
326-
offsetof(psync_fstask_mkdir_t, folderid)),
327-
psync_fstask_mkdir_t, tree);
328-
}
329-
330-
psync_fstask_creat_t *
331-
pfs_task_find_creat_by_fileid(psync_fstask_folder_t *folder,
332-
psync_fsfileid_t fileid) {
333-
return ptree_element(
334-
pfs_task_walk_tree(folder->creats, fileid,
335-
offsetof(psync_fstask_creat_t, fileid)),
336-
psync_fstask_creat_t, tree);
337-
}
338-
339196
static void pfs_task_depend(uint64_t taskid, uint64_t dependontaskid) {
340197
psync_sql_res *res;
341198
res = psql_prepare("INSERT OR IGNORE INTO fstaskdepend "

pclsync/pfstasks_tree.c

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
/*
2+
* pfstasks_tree.c — pure tree operations for the fstask subsystem.
3+
*
4+
* All functions here are psql-free and operate only on in-memory psync_tree
5+
* structures. Extracted so that unit tests can link this file alone without
6+
* pulling in pfstasks.c's heavy dependencies (psql, pcrypto, pfs*, …).
7+
*/
8+
9+
#include <stddef.h>
10+
#include <string.h>
11+
12+
#include "pdbg.h"
13+
#include "pfstasks_tree.h"
14+
15+
/* ------------------------------------------------------------------ */
16+
/* Static helpers */
17+
/* ------------------------------------------------------------------ */
18+
19+
/*
20+
* BST search by name string at `nameoff`.
21+
* If `taskid` != 0 and a name-equal node has a different taskid,
22+
* walks prev/next siblings to find the matching one.
23+
*/
24+
static psync_tree *pfs_task_search_tree(psync_tree *tree, size_t nameoff,
25+
const char *name, uint64_t taskid,
26+
size_t taskidoff) {
27+
int c;
28+
while (tree) {
29+
c = strcmp(name, ((char *)tree) + nameoff);
30+
if (c < 0) tree = tree->left;
31+
else if (c > 0) tree = tree->right;
32+
else break;
33+
}
34+
if (!tree || !taskid ||
35+
*((uint64_t *)(((char *)tree) + taskidoff)) == taskid)
36+
return tree;
37+
/* Walk siblings with the same name to find the matching taskid */
38+
psync_tree *tn = ptree_get_prev(tree);
39+
while (tn) {
40+
if (strcmp(name, ((char *)tn) + nameoff)) break;
41+
if (*((uint64_t *)(((char *)tn) + taskidoff)) == taskid) return tn;
42+
tn = ptree_get_prev(tn);
43+
}
44+
tn = ptree_get_next(tree);
45+
while (tn) {
46+
if (strcmp(name, ((char *)tn) + nameoff)) break;
47+
if (*((uint64_t *)(((char *)tn) + taskidoff)) == taskid) return tn;
48+
tn = ptree_get_next(tn);
49+
}
50+
return NULL;
51+
}
52+
53+
/*
54+
* Linear in-order walk to find the first node whose uint64_t field at
55+
* `taskidoff` equals `taskid`.
56+
*/
57+
static psync_tree *pfs_task_walk_tree(psync_tree *tree, uint64_t taskid,
58+
size_t taskidoff) {
59+
tree = ptree_get_first(tree);
60+
while (tree) {
61+
if (*((uint64_t *)(((char *)tree) + taskidoff)) == taskid) return tree;
62+
tree = ptree_get_next(tree);
63+
}
64+
return NULL;
65+
}
66+
67+
/* ------------------------------------------------------------------ */
68+
/* Exported: tree insertion */
69+
/* ------------------------------------------------------------------ */
70+
71+
void pfs_task_insert_into_tree(psync_tree **tree, size_t nameoff,
72+
psync_tree *element) {
73+
const char *name;
74+
psync_tree *node;
75+
int c;
76+
77+
if (!*tree) {
78+
ptree_add_after(tree, NULL, element);
79+
return;
80+
}
81+
82+
name = ((char *)element) + nameoff;
83+
node = *tree;
84+
85+
while (1) {
86+
c = strcmp(name, ((char *)node) + nameoff);
87+
if (c < 0) {
88+
if (node->left)
89+
node = node->left;
90+
else {
91+
ptree_add_before(tree, node, element);
92+
return;
93+
}
94+
} else {
95+
if (c == 0)
96+
pdbg_logf(D_WARNING, "duplicate entry %s, should not happen",
97+
name);
98+
if (node->right)
99+
node = node->right;
100+
else {
101+
ptree_add_after(tree, node, element);
102+
return;
103+
}
104+
}
105+
}
106+
}
107+
108+
/* ------------------------------------------------------------------ */
109+
/* Exported: find by name (+ optional taskid discriminator) */
110+
/* ------------------------------------------------------------------ */
111+
112+
psync_fstask_mkdir_t *pfs_task_find_mkdir(psync_fstask_folder_t *folder,
113+
const char *name, uint64_t taskid) {
114+
return ptree_element(
115+
pfs_task_search_tree(folder->mkdirs,
116+
offsetof(psync_fstask_mkdir_t, name), name,
117+
taskid, offsetof(psync_fstask_mkdir_t, taskid)),
118+
psync_fstask_mkdir_t, tree);
119+
}
120+
121+
psync_fstask_rmdir_t *pfs_task_find_rmdir(psync_fstask_folder_t *folder,
122+
const char *name, uint64_t taskid) {
123+
return ptree_element(
124+
pfs_task_search_tree(folder->rmdirs,
125+
offsetof(psync_fstask_rmdir_t, name), name,
126+
taskid, offsetof(psync_fstask_rmdir_t, taskid)),
127+
psync_fstask_rmdir_t, tree);
128+
}
129+
130+
psync_fstask_creat_t *pfs_task_find_creat(psync_fstask_folder_t *folder,
131+
const char *name, uint64_t taskid) {
132+
return ptree_element(
133+
pfs_task_search_tree(folder->creats,
134+
offsetof(psync_fstask_creat_t, name), name,
135+
taskid, offsetof(psync_fstask_creat_t, taskid)),
136+
psync_fstask_creat_t, tree);
137+
}
138+
139+
psync_fstask_unlink_t *pfs_task_find_unlink(psync_fstask_folder_t *folder,
140+
const char *name, uint64_t taskid) {
141+
return ptree_element(
142+
pfs_task_search_tree(folder->unlinks,
143+
offsetof(psync_fstask_unlink_t, name), name,
144+
taskid, offsetof(psync_fstask_unlink_t, taskid)),
145+
psync_fstask_unlink_t, tree);
146+
}
147+
148+
/* ------------------------------------------------------------------ */
149+
/* Exported: find by numeric ID */
150+
/* ------------------------------------------------------------------ */
151+
152+
psync_fstask_mkdir_t *pfs_task_find_mkdir_by_folderid(
153+
psync_fstask_folder_t *folder, psync_fsfolderid_t folderid) {
154+
return ptree_element(
155+
pfs_task_walk_tree(folder->mkdirs, folderid,
156+
offsetof(psync_fstask_mkdir_t, folderid)),
157+
psync_fstask_mkdir_t, tree);
158+
}
159+
160+
psync_fstask_creat_t *pfs_task_find_creat_by_fileid(
161+
psync_fstask_folder_t *folder, psync_fsfileid_t fileid) {
162+
return ptree_element(
163+
pfs_task_walk_tree(folder->creats, fileid,
164+
offsetof(psync_fstask_creat_t, fileid)),
165+
psync_fstask_creat_t, tree);
166+
}

0 commit comments

Comments
 (0)