-
Notifications
You must be signed in to change notification settings - Fork 7
/
fuse_node.c
273 lines (228 loc) · 8.18 KB
/
fuse_node.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
/*
* Copyright (C) 2006-2008 Google. All Rights Reserved.
* Copyright (C) 2010 Tuxera. All Rights Reserved.
*/
#include "fuse.h"
#include "fuse_internal.h"
#include "fuse_ipc.h"
#include "fuse_locking.h"
#include "fuse_node.h"
#include "fuse_sysctl.h"
#include "compat/tree.h"
#include <stdbool.h>
#ifdef FUSE4X_ENABLE_BIGLOCK
#include "fuse_biglock_vnops.h"
#endif
static int fuse_vnode_compare(struct fuse_vnode_data *d1, struct fuse_vnode_data *d2)
{
if (d1->nodeid > d2->nodeid) {
return 1;
} else if (d1->nodeid == d2->nodeid) {
return 0;
} else {
return -1;
}
}
RB_GENERATE(fuse_data_nodes, fuse_vnode_data, nodes_link, fuse_vnode_compare);
void
fuse_vnode_data_destroy(struct fuse_vnode_data *fvdat)
{
#ifdef FUSE4X_ENABLE_TSLOCKING
lck_rw_free(fvdat->nodelock, fuse_lock_group);
lck_rw_free(fvdat->truncatelock, fuse_lock_group);
#endif
bzero(fvdat, sizeof(*fvdat));
FUSE_OSFree(fvdat, sizeof(*fvdat), fuse_malloc_tag);
}
errno_t
FSNodeGetOrCreateFileVNodeByID(vnode_t *vnPtr,
bool is_root,
struct fuse_entry_out *feo,
mount_t mp,
vnode_t dvp,
vfs_context_t context,
uint32_t *oflags)
{
int err = 0;
vnode_t vn = NULLVP;
uint32_t vid = 0;
struct fuse_data *mntdata = NULL;
enum vtype vtyp = IFTOVT(feo->attr.mode);
if ((vtyp >= VBAD) || (vtyp == VNON)) {
return EINVAL;
}
uint64_t size = is_root ? 0 : feo->attr.size;
uint64_t generation = feo->generation;
mntdata = fuse_get_mpdata(mp);
struct fuse_vnode_data tt = {
.nodeid = feo->nodeid
};
fuse_lck_mtx_lock(mntdata->node_mtx);
struct fuse_vnode_data *fvdat = RB_FIND(fuse_data_nodes, &mntdata->nodes_head, &tt);
if (fvdat) {
vn = fvdat->vp;
vid = vnode_vid(vn);
}
fuse_lck_mtx_unlock(mntdata->node_mtx);
if (vn) {
int geterr = vnode_getwithvid(vn, vid);
if (geterr == ENOENT) {
// What happened here is a race condition between this function and vnode reclaiming.
// We do not increase a usage counter when we put nodes to rb-tree.
// So vnode can be reclaimed by kernel at any time.
// Let's think what heppens when this function is called at the very same time as reclaim.
// T(this process), R(reclaim process)
// T - find vnode in rb-tree
// R - remove vnode from rb-tree
// R - free fuse_vnode_data structure
// R - reclaim vnode and let someone else use it, let's say process O
// O - call vnode_create() and reuse vnode reclaimed above
// T - call vnode_get() for vnode we got from rb-tree at the step one but now used by process O. bummer!!!
// The problem is that vnode that we try to get() is completely different from the one that we
// had in rb-tree at the beginning of the process.
// To avoid this race condition we need to store and check vid. vid is some kind of vnode identifier -
// once a vnode is reclaimend this id is changed. If vnode reclaim happened then vnode_getwithvid()
// above fails with ENOENT error. In such case we just ignore the vnode and perform a new vnode creation.
vn = NULLVP;
}
}
if (!vn) {
fvdat = FUSE_OSMalloc(sizeof(*fvdat), fuse_malloc_tag);
bzero(fvdat, sizeof(*fvdat));
struct vnode_fsparam params;
/* self */
fvdat->vp = NULLVP; /* hold on */
fvdat->nodeid = feo->nodeid;
fvdat->generation = generation;
/* parent */
fvdat->parentvp = dvp;
if (dvp) {
fvdat->parent_nodeid = VTOI(dvp);
} else {
fvdat->parent_nodeid = 0;
}
/* I/O */
for (int k = 0; k < FUFH_MAXTYPE; k++) {
FUFH_USE_RESET(&(fvdat->fufh[k]));
}
/* flags */
fvdat->flag = 0;
fvdat->c_flag = 0;
/* meta */
/* XXX: truncation */
fvdat->entry_valid.tv_sec = (time_t)feo->entry_valid;
fvdat->entry_valid.tv_nsec = feo->entry_valid_nsec;
/* XXX: truncation */
fvdat->attr_valid.tv_sec = (time_t)feo->attr_valid;
fvdat->attr_valid.tv_nsec = feo->attr_valid_nsec;
/* XXX: truncation */
fvdat->modify_time.tv_sec = (time_t)feo->attr.mtime;
fvdat->modify_time.tv_nsec = feo->attr.mtimensec;
fvdat->filesize = size;
fvdat->nlookup = 0;
fvdat->vtype = vtyp;
#ifdef FUSE4X_ENABLE_TSLOCKING
fvdat->nodelock = lck_rw_alloc_init(fuse_lock_group,
fuse_lock_attr);
fvdat->nodelockowner = NULL;
fvdat->truncatelock = lck_rw_alloc_init(fuse_lock_group,
fuse_lock_attr);
#endif
params.vnfs_mp = mp;
params.vnfs_vtype = vtyp;
params.vnfs_str = NULL;
params.vnfs_dvp = dvp; /* NULLVP for the root vnode */
params.vnfs_fsnode = fvdat;
params.vnfs_vops = fuse_vnode_operations;
params.vnfs_rdev = 0;
params.vnfs_marksystem = 0;
params.vnfs_cnp = NULL;
params.vnfs_flags = VNFS_NOCACHE | VNFS_CANTCACHE;
params.vnfs_filesize = size;
params.vnfs_markroot = is_root ? 1 : 0;
#ifdef FUSE4X_ENABLE_BIGLOCK
fuse_biglock_unlock(mntdata->biglock);
#endif
err = vnode_create(VNCREATE_FLAVOR, (uint32_t)sizeof(params),
¶ms, &vn);
#ifdef FUSE4X_ENABLE_BIGLOCK
fuse_biglock_lock(mntdata->biglock);
#endif
if (err == 0) {
if (is_root) {
fvdat->parentvp = vn;
} else {
fvdat->parentvp = dvp;
}
if (oflags) {
*oflags |= MAKEENTRY;
}
fvdat->vp = vn;
fvdat->vid = vnode_vid(vn);
fuse_lck_mtx_lock(mntdata->node_mtx);
RB_INSERT(fuse_data_nodes, &mntdata->nodes_head, fvdat);
fuse_lck_mtx_unlock(mntdata->node_mtx);
vnode_addfsref(vn);
OSIncrementAtomic((SInt32 *)&fuse_vnodes_current);
} else {
log("fuse4x: vnode (ino=%llu) cannot be created, err=%d\n", feo->nodeid, err);
fuse_vnode_data_destroy(fvdat);
}
}
if (err == 0) {
if (vnode_vtype(vn) != vtyp) {
log("fuse4x: vnode changed type behind us (old=%d, new=%d)\n",
vnode_vtype(vn), vtyp);
#ifdef FUSE4X_ENABLE_BIGLOCK
fuse_biglock_unlock(mntdata->biglock);
#endif
fuse_vncache_purge(vn);
#ifdef FUSE4X_ENABLE_BIGLOCK
fuse_biglock_lock(mntdata->biglock);
#endif
vnode_put(vn);
err = EIO;
} else if (VTOFUD(vn)->generation != generation) {
log("fuse4x: vnode changed generation\n");
#ifdef FUSE4X_ENABLE_BIGLOCK
fuse_biglock_unlock(mntdata->biglock);
#endif
fuse_vncache_purge(vn);
#ifdef FUSE4X_ENABLE_BIGLOCK
fuse_biglock_lock(mntdata->biglock);
#endif
vnode_put(vn);
err = ESTALE;
}
}
if (err == 0) {
*vnPtr = vn;
/* Need VT_FUSE4X from xnu */
vnode_settag(vn, VT_OTHER);
}
return err;
}
int
fuse_vget_i(vnode_t *vpp,
struct fuse_entry_out *feo,
struct componentname *cnp,
vnode_t dvp,
mount_t mp,
vfs_context_t context)
{
int err = 0;
if (!feo) {
return EINVAL;
}
err = FSNodeGetOrCreateFileVNodeByID(vpp, false, feo, mp, dvp,
context, NULL);
if (err) {
return err;
}
if (cnp->cn_flags & MAKEENTRY) {
fuse_vncache_enter(dvp, *vpp, cnp);
}
/* found: */
VTOFUD(*vpp)->nlookup++;
return 0;
}