]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | ||
4 | #include "Client.h" | |
5 | #include "Inode.h" | |
6 | #include "Dentry.h" | |
7 | #include "Dir.h" | |
8 | #include "Fh.h" | |
9 | #include "MetaSession.h" | |
10 | #include "ClientSnapRealm.h" | |
b32b8144 | 11 | #include "Delegation.h" |
7c673cae FG |
12 | |
13 | #include "mds/flock.h" | |
14 | ||
15 | Inode::~Inode() | |
16 | { | |
28e407b8 AA |
17 | delay_cap_item.remove_myself(); |
18 | dirty_cap_item.remove_myself(); | |
7c673cae FG |
19 | snaprealm_item.remove_myself(); |
20 | ||
21 | if (snapdir_parent) { | |
22 | snapdir_parent->flags &= ~I_SNAPDIR_OPEN; | |
23 | snapdir_parent.reset(); | |
24 | } | |
25 | ||
26 | if (!oset.objects.empty()) { | |
27 | lsubdout(client->cct, client, 0) << __func__ << ": leftover objects on inode 0x" | |
28 | << std::hex << ino << std::dec << dendl; | |
29 | assert(oset.objects.empty()); | |
30 | } | |
31 | ||
b32b8144 FG |
32 | if (!delegations.empty()) { |
33 | lsubdout(client->cct, client, 0) << __func__ << ": leftover delegations on inode 0x" | |
34 | << std::hex << ino << std::dec << dendl; | |
35 | assert(delegations.empty()); | |
36 | } | |
37 | ||
7c673cae FG |
38 | delete fcntl_locks; |
39 | delete flock_locks; | |
40 | } | |
41 | ||
42 | ostream& operator<<(ostream &out, const Inode &in) | |
43 | { | |
44 | out << in.vino() << "(" | |
45 | << "faked_ino=" << in.faked_ino | |
46 | << " ref=" << in._ref | |
47 | << " ll_ref=" << in.ll_ref | |
48 | << " cap_refs=" << in.cap_refs | |
49 | << " open=" << in.open_by_mode | |
50 | << " mode=" << oct << in.mode << dec | |
51 | << " size=" << in.size << "/" << in.max_size | |
28e407b8 | 52 | << " nlink=" << in.nlink |
7c673cae FG |
53 | << " mtime=" << in.mtime |
54 | << " caps=" << ccap_string(in.caps_issued()); | |
55 | if (!in.caps.empty()) { | |
56 | out << "("; | |
57 | for (auto p = in.caps.begin(); p != in.caps.end(); ++p) { | |
58 | if (p != in.caps.begin()) | |
59 | out << ','; | |
60 | out << p->first << '=' << ccap_string(p->second->issued); | |
61 | } | |
62 | out << ")"; | |
63 | } | |
64 | if (in.dirty_caps) | |
65 | out << " dirty_caps=" << ccap_string(in.dirty_caps); | |
66 | if (in.flushing_caps) | |
67 | out << " flushing_caps=" << ccap_string(in.flushing_caps); | |
68 | ||
69 | if (in.flags & I_COMPLETE) | |
70 | out << " COMPLETE"; | |
71 | ||
72 | if (in.is_file()) | |
73 | out << " " << in.oset; | |
74 | ||
75 | if (!in.dn_set.empty()) | |
76 | out << " parents=" << in.dn_set; | |
77 | ||
78 | if (in.is_dir() && in.has_dir_layout()) | |
79 | out << " has_dir_layout"; | |
80 | ||
81 | if (in.quota.is_enable()) | |
82 | out << " " << in.quota; | |
83 | ||
84 | out << ' ' << &in << ")"; | |
85 | return out; | |
86 | } | |
87 | ||
88 | ||
89 | void Inode::make_long_path(filepath& p) | |
90 | { | |
91 | if (!dn_set.empty()) { | |
92 | assert((*dn_set.begin())->dir && (*dn_set.begin())->dir->parent_inode); | |
93 | (*dn_set.begin())->dir->parent_inode->make_long_path(p); | |
94 | p.push_dentry((*dn_set.begin())->name); | |
95 | } else if (snapdir_parent) { | |
96 | snapdir_parent->make_nosnap_relative_path(p); | |
97 | string empty; | |
98 | p.push_dentry(empty); | |
99 | } else | |
100 | p = filepath(ino); | |
101 | } | |
102 | ||
103 | /* | |
104 | * make a filepath suitable for an mds request: | |
105 | * - if we are non-snapped/live, the ino is sufficient, e.g. #1234 | |
106 | * - if we are snapped, make filepath relative to first non-snapped parent. | |
107 | */ | |
108 | void Inode::make_nosnap_relative_path(filepath& p) | |
109 | { | |
110 | if (snapid == CEPH_NOSNAP) { | |
111 | p = filepath(ino); | |
112 | } else if (snapdir_parent) { | |
113 | snapdir_parent->make_nosnap_relative_path(p); | |
114 | string empty; | |
115 | p.push_dentry(empty); | |
116 | } else if (!dn_set.empty()) { | |
117 | assert((*dn_set.begin())->dir && (*dn_set.begin())->dir->parent_inode); | |
118 | (*dn_set.begin())->dir->parent_inode->make_nosnap_relative_path(p); | |
119 | p.push_dentry((*dn_set.begin())->name); | |
120 | } else { | |
121 | p = filepath(ino); | |
122 | } | |
123 | } | |
124 | ||
125 | void Inode::get_open_ref(int mode) | |
126 | { | |
127 | open_by_mode[mode]++; | |
b32b8144 | 128 | break_deleg(!(mode & CEPH_FILE_MODE_WR)); |
7c673cae FG |
129 | } |
130 | ||
131 | bool Inode::put_open_ref(int mode) | |
132 | { | |
133 | //cout << "open_by_mode[" << mode << "] " << open_by_mode[mode] << " -> " << (open_by_mode[mode]-1) << std::endl; | |
134 | if (--open_by_mode[mode] == 0) | |
135 | return true; | |
136 | return false; | |
137 | } | |
138 | ||
139 | void Inode::get_cap_ref(int cap) | |
140 | { | |
141 | int n = 0; | |
142 | while (cap) { | |
143 | if (cap & 1) { | |
144 | int c = 1 << n; | |
145 | cap_refs[c]++; | |
146 | //cout << "inode " << *this << " get " << cap_string(c) << " " << (cap_refs[c]-1) << " -> " << cap_refs[c] << std::endl; | |
147 | } | |
148 | cap >>= 1; | |
149 | n++; | |
150 | } | |
151 | } | |
152 | ||
153 | int Inode::put_cap_ref(int cap) | |
154 | { | |
7c673cae FG |
155 | int last = 0; |
156 | int n = 0; | |
157 | while (cap) { | |
158 | if (cap & 1) { | |
159 | int c = 1 << n; | |
160 | if (cap_refs[c] <= 0) { | |
161 | lderr(client->cct) << "put_cap_ref " << ccap_string(c) << " went negative on " << *this << dendl; | |
162 | assert(cap_refs[c] > 0); | |
163 | } | |
164 | if (--cap_refs[c] == 0) | |
165 | last |= c; | |
166 | //cout << "inode " << *this << " put " << cap_string(c) << " " << (cap_refs[c]+1) << " -> " << cap_refs[c] << std::endl; | |
167 | } | |
168 | cap >>= 1; | |
169 | n++; | |
170 | } | |
171 | return last; | |
172 | } | |
173 | ||
174 | bool Inode::is_any_caps() | |
175 | { | |
176 | return !caps.empty() || snap_caps; | |
177 | } | |
178 | ||
179 | bool Inode::cap_is_valid(Cap* cap) const | |
180 | { | |
181 | /*cout << "cap_gen " << cap->session-> cap_gen << std::endl | |
182 | << "session gen " << cap->gen << std::endl | |
183 | << "cap expire " << cap->session->cap_ttl << std::endl | |
184 | << "cur time " << ceph_clock_now(cct) << std::endl;*/ | |
185 | if ((cap->session->cap_gen <= cap->gen) | |
186 | && (ceph_clock_now() < cap->session->cap_ttl)) { | |
187 | return true; | |
188 | } | |
189 | return false; | |
190 | } | |
191 | ||
192 | int Inode::caps_issued(int *implemented) const | |
193 | { | |
194 | int c = snap_caps; | |
195 | int i = 0; | |
196 | for (map<mds_rank_t,Cap*>::const_iterator it = caps.begin(); | |
197 | it != caps.end(); | |
198 | ++it) | |
199 | if (cap_is_valid(it->second)) { | |
200 | c |= it->second->issued; | |
201 | i |= it->second->implemented; | |
202 | } | |
94b18763 FG |
203 | |
204 | // exclude caps issued by non-auth MDS, but are been revoking by | |
205 | // the auth MDS. The non-auth MDS should be revoking/exporting | |
206 | // these caps, but the message is delayed. | |
207 | if (auth_cap) | |
208 | c &= ~auth_cap->implemented | auth_cap->issued; | |
209 | ||
7c673cae FG |
210 | if (implemented) |
211 | *implemented = i; | |
212 | return c; | |
213 | } | |
214 | ||
215 | void Inode::touch_cap(Cap *cap) | |
216 | { | |
217 | // move to back of LRU | |
218 | cap->session->caps.push_back(&cap->cap_item); | |
219 | } | |
220 | ||
221 | void Inode::try_touch_cap(mds_rank_t mds) | |
222 | { | |
223 | if (caps.count(mds)) | |
224 | touch_cap(caps[mds]); | |
225 | } | |
226 | ||
94b18763 FG |
227 | /** |
228 | * caps_issued_mask - check whether we have all of the caps in the mask | |
229 | * @mask: mask to check against | |
230 | * @allow_impl: whether the caller can also use caps that are implemented but not issued | |
231 | * | |
232 | * This is the bog standard "check whether we have the required caps" operation. | |
233 | * Typically, we only check against the capset that is currently "issued". | |
234 | * In other words, we ignore caps that have been revoked but not yet released. | |
235 | * | |
236 | * Some callers (particularly those doing attribute retrieval) can also make | |
237 | * use of the full set of "implemented" caps to satisfy requests from the | |
238 | * cache. | |
239 | * | |
240 | * Those callers should refrain from taking new references to implemented | |
241 | * caps! | |
242 | */ | |
243 | bool Inode::caps_issued_mask(unsigned mask, bool allow_impl) | |
7c673cae FG |
244 | { |
245 | int c = snap_caps; | |
94b18763 FG |
246 | int i = 0; |
247 | ||
7c673cae FG |
248 | if ((c & mask) == mask) |
249 | return true; | |
250 | // prefer auth cap | |
251 | if (auth_cap && | |
252 | cap_is_valid(auth_cap) && | |
253 | (auth_cap->issued & mask) == mask) { | |
254 | touch_cap(auth_cap); | |
255 | return true; | |
256 | } | |
257 | // try any cap | |
258 | for (map<mds_rank_t,Cap*>::iterator it = caps.begin(); | |
259 | it != caps.end(); | |
260 | ++it) { | |
261 | if (cap_is_valid(it->second)) { | |
262 | if ((it->second->issued & mask) == mask) { | |
263 | touch_cap(it->second); | |
264 | return true; | |
265 | } | |
266 | c |= it->second->issued; | |
94b18763 | 267 | i |= it->second->implemented; |
7c673cae FG |
268 | } |
269 | } | |
94b18763 FG |
270 | |
271 | if (allow_impl) | |
272 | c |= i; | |
273 | ||
7c673cae FG |
274 | if ((c & mask) == mask) { |
275 | // bah.. touch them all | |
276 | for (map<mds_rank_t,Cap*>::iterator it = caps.begin(); | |
277 | it != caps.end(); | |
278 | ++it) | |
279 | touch_cap(it->second); | |
280 | return true; | |
281 | } | |
282 | return false; | |
283 | } | |
284 | ||
285 | int Inode::caps_used() | |
286 | { | |
287 | int w = 0; | |
288 | for (map<int,int>::iterator p = cap_refs.begin(); | |
289 | p != cap_refs.end(); | |
290 | ++p) | |
291 | if (p->second) | |
292 | w |= p->first; | |
293 | return w; | |
294 | } | |
295 | ||
296 | int Inode::caps_file_wanted() | |
297 | { | |
298 | int want = 0; | |
299 | for (map<int,int>::iterator p = open_by_mode.begin(); | |
300 | p != open_by_mode.end(); | |
301 | ++p) | |
302 | if (p->second) | |
303 | want |= ceph_caps_for_mode(p->first); | |
304 | return want; | |
305 | } | |
306 | ||
307 | int Inode::caps_wanted() | |
308 | { | |
309 | int want = caps_file_wanted() | caps_used(); | |
310 | if (want & CEPH_CAP_FILE_BUFFER) | |
311 | want |= CEPH_CAP_FILE_EXCL; | |
312 | return want; | |
313 | } | |
314 | ||
315 | int Inode::caps_mds_wanted() | |
316 | { | |
317 | int want = 0; | |
318 | for (auto it = caps.begin(); it != caps.end(); ++it) | |
319 | want |= it->second->wanted; | |
320 | return want; | |
321 | } | |
322 | ||
323 | int Inode::caps_dirty() | |
324 | { | |
325 | return dirty_caps | flushing_caps; | |
326 | } | |
327 | ||
328 | const UserPerm* Inode::get_best_perms() | |
329 | { | |
330 | const UserPerm *perms = NULL; | |
331 | for (const auto ci : caps) { | |
332 | const UserPerm& iperm = ci.second->latest_perms; | |
333 | if (!perms) { // we don't have any, take what's present | |
334 | perms = &iperm; | |
335 | } else if (iperm.uid() == uid) { | |
336 | if (iperm.gid() == gid) { // we have the best possible, return | |
337 | return &iperm; | |
338 | } | |
339 | if (perms->uid() != uid) { // take uid > gid every time | |
340 | perms = &iperm; | |
341 | } | |
342 | } else if (perms->uid() != uid && iperm.gid() == gid) { | |
343 | perms = &iperm; // a matching gid is better than nothing | |
344 | } | |
345 | } | |
346 | return perms; | |
347 | } | |
348 | ||
349 | bool Inode::have_valid_size() | |
350 | { | |
351 | // RD+RDCACHE or WR+WRBUFFER => valid size | |
352 | if (caps_issued() & (CEPH_CAP_FILE_SHARED | CEPH_CAP_FILE_EXCL)) | |
353 | return true; | |
354 | return false; | |
355 | } | |
356 | ||
357 | // open Dir for an inode. if it's not open, allocated it (and pin dentry in memory). | |
358 | Dir *Inode::open_dir() | |
359 | { | |
360 | if (!dir) { | |
361 | dir = new Dir(this); | |
362 | lsubdout(client->cct, client, 15) << "open_dir " << dir << " on " << this << dendl; | |
363 | assert(dn_set.size() < 2); // dirs can't be hard-linked | |
364 | if (!dn_set.empty()) | |
365 | (*dn_set.begin())->get(); // pin dentry | |
366 | get(); // pin inode | |
367 | } | |
368 | return dir; | |
369 | } | |
370 | ||
371 | bool Inode::check_mode(const UserPerm& perms, unsigned want) | |
372 | { | |
373 | if (uid == perms.uid()) { | |
374 | // if uid is owner, owner entry determines access | |
375 | want = want << 6; | |
376 | } else if (perms.gid_in_groups(gid)) { | |
377 | // if a gid or sgid matches the owning group, group entry determines access | |
378 | want = want << 3; | |
379 | } | |
380 | ||
381 | return (mode & want) == want; | |
382 | } | |
383 | ||
384 | void Inode::get() { | |
385 | _ref++; | |
386 | lsubdout(client->cct, client, 15) << "inode.get on " << this << " " << ino << '.' << snapid | |
387 | << " now " << _ref << dendl; | |
388 | } | |
389 | ||
390 | //private method to put a reference; see Client::put_inode() | |
391 | int Inode::_put(int n) { | |
392 | _ref -= n; | |
393 | lsubdout(client->cct, client, 15) << "inode.put on " << this << " " << ino << '.' << snapid | |
394 | << " now " << _ref << dendl; | |
395 | assert(_ref >= 0); | |
396 | return _ref; | |
397 | } | |
398 | ||
399 | ||
400 | void Inode::dump(Formatter *f) const | |
401 | { | |
402 | f->dump_stream("ino") << ino; | |
403 | f->dump_stream("snapid") << snapid; | |
404 | if (rdev) | |
405 | f->dump_unsigned("rdev", rdev); | |
406 | f->dump_stream("ctime") << ctime; | |
407 | f->dump_stream("btime") << btime; | |
408 | f->dump_stream("mode") << '0' << std::oct << mode << std::dec; | |
409 | f->dump_unsigned("uid", uid); | |
410 | f->dump_unsigned("gid", gid); | |
31f18b77 | 411 | f->dump_int("nlink", nlink); |
7c673cae | 412 | |
31f18b77 FG |
413 | f->dump_unsigned("size", size); |
414 | f->dump_unsigned("max_size", max_size); | |
415 | f->dump_unsigned("truncate_seq", truncate_seq); | |
416 | f->dump_unsigned("truncate_size", truncate_size); | |
7c673cae FG |
417 | f->dump_stream("mtime") << mtime; |
418 | f->dump_stream("atime") << atime; | |
31f18b77 FG |
419 | f->dump_unsigned("time_warp_seq", time_warp_seq); |
420 | f->dump_unsigned("change_attr", change_attr); | |
7c673cae FG |
421 | |
422 | f->dump_object("layout", layout); | |
423 | if (is_dir()) { | |
424 | f->open_object_section("dir_layout"); | |
425 | ::dump(dir_layout, f); | |
426 | f->close_section(); | |
427 | ||
428 | f->dump_bool("complete", flags & I_COMPLETE); | |
429 | f->dump_bool("ordered", flags & I_DIR_ORDERED); | |
430 | ||
431 | /* FIXME when wip-mds-encoding is merged *** | |
432 | f->open_object_section("dir_stat"); | |
433 | dirstat.dump(f); | |
434 | f->close_section(); | |
435 | ||
436 | f->open_object_section("rstat"); | |
437 | rstat.dump(f); | |
438 | f->close_section(); | |
439 | */ | |
440 | } | |
441 | ||
442 | f->dump_unsigned("version", version); | |
443 | f->dump_unsigned("xattr_version", xattr_version); | |
444 | f->dump_unsigned("flags", flags); | |
445 | ||
446 | if (is_dir()) { | |
447 | if (!dir_contacts.empty()) { | |
448 | f->open_object_section("dir_contants"); | |
449 | for (set<int>::iterator p = dir_contacts.begin(); p != dir_contacts.end(); ++p) | |
450 | f->dump_int("mds", *p); | |
451 | f->close_section(); | |
452 | } | |
453 | f->dump_int("dir_hashed", (int)dir_hashed); | |
454 | f->dump_int("dir_replicated", (int)dir_replicated); | |
455 | } | |
456 | ||
457 | f->open_array_section("caps"); | |
458 | for (map<mds_rank_t,Cap*>::const_iterator p = caps.begin(); p != caps.end(); ++p) { | |
459 | f->open_object_section("cap"); | |
460 | f->dump_int("mds", p->first); | |
461 | if (p->second == auth_cap) | |
462 | f->dump_int("auth", 1); | |
463 | p->second->dump(f); | |
464 | f->close_section(); | |
465 | } | |
466 | f->close_section(); | |
467 | if (auth_cap) | |
468 | f->dump_int("auth_cap", auth_cap->session->mds_num); | |
469 | ||
470 | f->dump_stream("dirty_caps") << ccap_string(dirty_caps); | |
471 | if (flushing_caps) { | |
472 | f->dump_stream("flushings_caps") << ccap_string(flushing_caps); | |
473 | f->open_object_section("flushing_cap_tid"); | |
474 | for (map<ceph_tid_t, int>::const_iterator p = flushing_cap_tids.begin(); | |
475 | p != flushing_cap_tids.end(); | |
476 | ++p) { | |
477 | string n(ccap_string(p->second)); | |
478 | f->dump_unsigned(n.c_str(), p->first); | |
479 | } | |
480 | f->close_section(); | |
481 | } | |
482 | f->dump_int("shared_gen", shared_gen); | |
483 | f->dump_int("cache_gen", cache_gen); | |
484 | if (snap_caps) { | |
485 | f->dump_int("snap_caps", snap_caps); | |
486 | f->dump_int("snap_cap_refs", snap_cap_refs); | |
487 | } | |
488 | ||
489 | f->dump_stream("hold_caps_until") << hold_caps_until; | |
490 | ||
491 | if (snaprealm) { | |
492 | f->open_object_section("snaprealm"); | |
493 | snaprealm->dump(f); | |
494 | f->close_section(); | |
495 | } | |
496 | if (!cap_snaps.empty()) { | |
497 | for (const auto &p : cap_snaps) { | |
498 | f->open_object_section("cap_snap"); | |
499 | f->dump_stream("follows") << p.first; | |
500 | p.second.dump(f); | |
501 | f->close_section(); | |
502 | } | |
503 | } | |
504 | ||
505 | // open | |
506 | if (!open_by_mode.empty()) { | |
507 | f->open_array_section("open_by_mode"); | |
508 | for (map<int,int>::const_iterator p = open_by_mode.begin(); p != open_by_mode.end(); ++p) { | |
509 | f->open_object_section("ref"); | |
31f18b77 FG |
510 | f->dump_int("mode", p->first); |
511 | f->dump_int("refs", p->second); | |
7c673cae FG |
512 | f->close_section(); |
513 | } | |
514 | f->close_section(); | |
515 | } | |
516 | if (!cap_refs.empty()) { | |
517 | f->open_array_section("cap_refs"); | |
518 | for (map<int,int>::const_iterator p = cap_refs.begin(); p != cap_refs.end(); ++p) { | |
519 | f->open_object_section("cap_ref"); | |
520 | f->dump_stream("cap") << ccap_string(p->first); | |
521 | f->dump_int("refs", p->second); | |
522 | f->close_section(); | |
523 | } | |
524 | f->close_section(); | |
525 | } | |
526 | ||
527 | f->dump_unsigned("reported_size", reported_size); | |
528 | if (wanted_max_size != max_size) | |
529 | f->dump_unsigned("wanted_max_size", wanted_max_size); | |
530 | if (requested_max_size != max_size) | |
531 | f->dump_unsigned("requested_max_size", requested_max_size); | |
532 | ||
533 | f->dump_int("ref", _ref); | |
534 | f->dump_int("ll_ref", ll_ref); | |
535 | ||
536 | if (!dn_set.empty()) { | |
537 | f->open_array_section("parents"); | |
538 | for (set<Dentry*>::const_iterator p = dn_set.begin(); p != dn_set.end(); ++p) { | |
539 | f->open_object_section("dentry"); | |
540 | f->dump_stream("dir_ino") << (*p)->dir->parent_inode->ino; | |
541 | f->dump_string("name", (*p)->name); | |
542 | f->close_section(); | |
543 | } | |
544 | f->close_section(); | |
545 | } | |
546 | } | |
547 | ||
548 | void Cap::dump(Formatter *f) const | |
549 | { | |
550 | f->dump_int("mds", session->mds_num); | |
551 | f->dump_stream("ino") << inode->ino; | |
552 | f->dump_unsigned("cap_id", cap_id); | |
553 | f->dump_stream("issued") << ccap_string(issued); | |
554 | if (implemented != issued) | |
555 | f->dump_stream("implemented") << ccap_string(implemented); | |
556 | f->dump_stream("wanted") << ccap_string(wanted); | |
557 | f->dump_unsigned("seq", seq); | |
558 | f->dump_unsigned("issue_seq", issue_seq); | |
559 | f->dump_unsigned("mseq", mseq); | |
560 | f->dump_unsigned("gen", gen); | |
561 | } | |
562 | ||
563 | void CapSnap::dump(Formatter *f) const | |
564 | { | |
565 | f->dump_stream("ino") << in->ino; | |
566 | f->dump_stream("issued") << ccap_string(issued); | |
567 | f->dump_stream("dirty") << ccap_string(dirty); | |
568 | f->dump_unsigned("size", size); | |
569 | f->dump_stream("ctime") << ctime; | |
570 | f->dump_stream("mtime") << mtime; | |
571 | f->dump_stream("atime") << atime; | |
572 | f->dump_int("time_warp_seq", time_warp_seq); | |
573 | f->dump_stream("mode") << '0' << std::oct << mode << std::dec; | |
574 | f->dump_unsigned("uid", uid); | |
575 | f->dump_unsigned("gid", gid); | |
576 | if (!xattrs.empty()) { | |
577 | f->open_object_section("xattr_lens"); | |
578 | for (map<string,bufferptr>::const_iterator p = xattrs.begin(); p != xattrs.end(); ++p) | |
579 | f->dump_int(p->first.c_str(), p->second.length()); | |
580 | f->close_section(); | |
581 | } | |
582 | f->dump_unsigned("xattr_version", xattr_version); | |
583 | f->dump_int("writing", (int)writing); | |
584 | f->dump_int("dirty_data", (int)dirty_data); | |
585 | f->dump_unsigned("flush_tid", flush_tid); | |
586 | } | |
587 | ||
588 | void Inode::set_async_err(int r) | |
589 | { | |
590 | for (const auto &fh : fhs) { | |
591 | fh->async_err = r; | |
592 | } | |
593 | } | |
594 | ||
b32b8144 FG |
595 | bool Inode::has_recalled_deleg() |
596 | { | |
597 | if (delegations.empty()) | |
598 | return false; | |
599 | ||
600 | // Either all delegations are recalled or none are. Just check the first. | |
601 | Delegation& deleg = delegations.front(); | |
602 | return deleg.is_recalled(); | |
603 | } | |
604 | ||
605 | void Inode::recall_deleg(bool skip_read) | |
606 | { | |
607 | if (delegations.empty()) | |
608 | return; | |
609 | ||
610 | // Issue any recalls | |
611 | for (list<Delegation>::iterator d = delegations.begin(); | |
612 | d != delegations.end(); ++d) { | |
613 | ||
614 | Delegation& deleg = *d; | |
615 | deleg.recall(skip_read); | |
616 | } | |
617 | } | |
618 | ||
619 | bool Inode::delegations_broken(bool skip_read) | |
620 | { | |
621 | if (delegations.empty()) { | |
622 | lsubdout(client->cct, client, 10) << | |
623 | __func__ << ": delegations empty on " << *this << dendl; | |
624 | return true; | |
625 | } | |
626 | ||
627 | if (skip_read) { | |
628 | Delegation& deleg = delegations.front(); | |
629 | lsubdout(client->cct, client, 10) << | |
630 | __func__ << ": read delegs only on " << *this << dendl; | |
631 | if (deleg.get_type() == CEPH_FILE_MODE_RD) { | |
632 | return true; | |
633 | } | |
634 | } | |
635 | lsubdout(client->cct, client, 10) << | |
636 | __func__ << ": not broken" << *this << dendl; | |
637 | return false; | |
638 | } | |
639 | ||
640 | void Inode::break_deleg(bool skip_read) | |
641 | { | |
642 | lsubdout(client->cct, client, 10) << | |
643 | __func__ << ": breaking delegs on " << *this << dendl; | |
644 | ||
645 | recall_deleg(skip_read); | |
646 | ||
647 | while (!delegations_broken(skip_read)) | |
648 | client->wait_on_list(waitfor_deleg); | |
649 | } | |
650 | ||
651 | /** | |
652 | * set_deleg: request a delegation on an open Fh | |
653 | * @fh: filehandle on which to acquire it | |
654 | * @type: delegation request type | |
655 | * @cb: delegation recall callback function | |
656 | * @priv: private pointer to be passed to callback | |
657 | * | |
658 | * Attempt to acquire a delegation on an open file handle. If there are no | |
659 | * conflicts and we have the right caps, allocate a new delegation, fill it | |
660 | * out and return 0. Return an error if we can't get one for any reason. | |
661 | */ | |
662 | int Inode::set_deleg(Fh *fh, unsigned type, ceph_deleg_cb_t cb, void *priv) | |
663 | { | |
664 | lsubdout(client->cct, client, 10) << | |
665 | __func__ << ": inode " << *this << dendl; | |
666 | ||
667 | /* | |
668 | * 0 deleg timeout means that they haven't been explicitly enabled. Don't | |
669 | * allow it, with an unusual error to make it clear. | |
670 | */ | |
671 | if (!client->get_deleg_timeout()) | |
672 | return -ETIME; | |
673 | ||
674 | // Just say no if we have any recalled delegs still outstanding | |
675 | if (has_recalled_deleg()) { | |
676 | lsubdout(client->cct, client, 10) << __func__ << | |
677 | ": has_recalled_deleg" << dendl; | |
678 | return -EAGAIN; | |
679 | } | |
680 | ||
681 | // check vs. currently open files on this inode | |
682 | switch (type) { | |
683 | case CEPH_DELEGATION_RD: | |
684 | if (open_count_for_write()) { | |
685 | lsubdout(client->cct, client, 10) << __func__ << | |
686 | ": open for write" << dendl; | |
687 | return -EAGAIN; | |
688 | } | |
689 | break; | |
690 | case CEPH_DELEGATION_WR: | |
691 | if (open_count() > 1) { | |
692 | lsubdout(client->cct, client, 10) << __func__ << ": open" << dendl; | |
693 | return -EAGAIN; | |
694 | } | |
695 | break; | |
696 | default: | |
697 | return -EINVAL; | |
698 | } | |
699 | ||
700 | /* | |
701 | * A delegation is essentially a long-held container for cap references that | |
702 | * we delegate to the client until recalled. The caps required depend on the | |
703 | * type of delegation (read vs. rw). This is entirely an opportunistic thing. | |
704 | * If we don't have the necessary caps for the delegation, then we just don't | |
705 | * grant one. | |
706 | * | |
707 | * In principle we could request the caps from the MDS, but a delegation is | |
708 | * usually requested just after an open. If we don't have the necessary caps | |
709 | * already, then it's likely that there is some sort of conflicting access. | |
710 | * | |
711 | * In the future, we may need to add a way to have this request caps more | |
712 | * aggressively -- for instance, to handle WANT_DELEGATION for NFSv4.1+. | |
713 | */ | |
714 | int need = ceph_deleg_caps_for_type(type); | |
715 | if (!caps_issued_mask(need)) { | |
716 | lsubdout(client->cct, client, 10) << __func__ << ": cap mismatch, have=" | |
717 | << ccap_string(caps_issued()) << " need=" << ccap_string(need) << dendl; | |
718 | return -EAGAIN; | |
719 | } | |
720 | ||
721 | for (list<Delegation>::iterator d = delegations.begin(); | |
722 | d != delegations.end(); ++d) { | |
723 | Delegation& deleg = *d; | |
724 | if (deleg.get_fh() == fh) { | |
725 | deleg.reinit(type, cb, priv); | |
726 | return 0; | |
727 | } | |
728 | } | |
729 | ||
730 | delegations.emplace_back(fh, type, cb, priv); | |
731 | return 0; | |
732 | } | |
733 | ||
734 | /** | |
735 | * unset_deleg - remove a delegation that was previously set | |
736 | * @fh: file handle to clear delegation of | |
737 | * | |
738 | * Unlink delegation from the Inode (if there is one), put caps and free it. | |
739 | */ | |
740 | void Inode::unset_deleg(Fh *fh) | |
741 | { | |
742 | for (list<Delegation>::iterator d = delegations.begin(); | |
743 | d != delegations.end(); ++d) { | |
744 | Delegation& deleg = *d; | |
745 | if (deleg.get_fh() == fh) { | |
746 | delegations.erase(d); | |
747 | client->signal_cond_list(waitfor_deleg); | |
748 | break; | |
749 | } | |
750 | } | |
751 | } | |
28e407b8 AA |
752 | |
753 | /** | |
754 | * mark_caps_dirty - mark some caps dirty | |
755 | * @caps: the dirty caps | |
756 | * | |
757 | * note that if there is no dirty and flushing caps before, we need to pin this inode. | |
758 | * it will be unpined by handle_cap_flush_ack when there are no dirty and flushing caps. | |
759 | */ | |
760 | void Inode::mark_caps_dirty(int caps) | |
761 | { | |
762 | lsubdout(client->cct, client, 10) << __func__ << " " << *this << " " << ccap_string(dirty_caps) << " -> " | |
763 | << ccap_string(dirty_caps | caps) << dendl; | |
764 | if (caps && !caps_dirty()) | |
765 | get(); | |
766 | dirty_caps |= caps; | |
767 | client->get_dirty_list().push_back(&dirty_cap_item); | |
768 | } | |
769 | ||
770 | /** | |
771 | * mark_caps_clean - only clean the dirty_caps and caller should start flushing the dirty caps. | |
772 | */ | |
773 | void Inode::mark_caps_clean() | |
774 | { | |
775 | lsubdout(client->cct, client, 10) << __func__ << " " << *this << dendl; | |
776 | dirty_caps = 0; | |
777 | dirty_cap_item.remove_myself(); | |
778 | } | |
779 | ||
780 |