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