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