]>
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" | |
11 | ||
12 | #include "mds/flock.h" | |
13 | ||
14 | Inode::~Inode() | |
15 | { | |
16 | cap_item.remove_myself(); | |
17 | snaprealm_item.remove_myself(); | |
18 | ||
19 | if (snapdir_parent) { | |
20 | snapdir_parent->flags &= ~I_SNAPDIR_OPEN; | |
21 | snapdir_parent.reset(); | |
22 | } | |
23 | ||
24 | if (!oset.objects.empty()) { | |
25 | lsubdout(client->cct, client, 0) << __func__ << ": leftover objects on inode 0x" | |
26 | << std::hex << ino << std::dec << dendl; | |
27 | assert(oset.objects.empty()); | |
28 | } | |
29 | ||
30 | delete fcntl_locks; | |
31 | delete flock_locks; | |
32 | } | |
33 | ||
34 | ostream& operator<<(ostream &out, const Inode &in) | |
35 | { | |
36 | out << in.vino() << "(" | |
37 | << "faked_ino=" << in.faked_ino | |
38 | << " ref=" << in._ref | |
39 | << " ll_ref=" << in.ll_ref | |
40 | << " cap_refs=" << in.cap_refs | |
41 | << " open=" << in.open_by_mode | |
42 | << " mode=" << oct << in.mode << dec | |
43 | << " size=" << in.size << "/" << in.max_size | |
44 | << " mtime=" << in.mtime | |
45 | << " caps=" << ccap_string(in.caps_issued()); | |
46 | if (!in.caps.empty()) { | |
47 | out << "("; | |
48 | for (auto p = in.caps.begin(); p != in.caps.end(); ++p) { | |
49 | if (p != in.caps.begin()) | |
50 | out << ','; | |
51 | out << p->first << '=' << ccap_string(p->second->issued); | |
52 | } | |
53 | out << ")"; | |
54 | } | |
55 | if (in.dirty_caps) | |
56 | out << " dirty_caps=" << ccap_string(in.dirty_caps); | |
57 | if (in.flushing_caps) | |
58 | out << " flushing_caps=" << ccap_string(in.flushing_caps); | |
59 | ||
60 | if (in.flags & I_COMPLETE) | |
61 | out << " COMPLETE"; | |
62 | ||
63 | if (in.is_file()) | |
64 | out << " " << in.oset; | |
65 | ||
66 | if (!in.dn_set.empty()) | |
67 | out << " parents=" << in.dn_set; | |
68 | ||
69 | if (in.is_dir() && in.has_dir_layout()) | |
70 | out << " has_dir_layout"; | |
71 | ||
72 | if (in.quota.is_enable()) | |
73 | out << " " << in.quota; | |
74 | ||
75 | out << ' ' << &in << ")"; | |
76 | return out; | |
77 | } | |
78 | ||
79 | ||
80 | void Inode::make_long_path(filepath& p) | |
81 | { | |
82 | if (!dn_set.empty()) { | |
83 | assert((*dn_set.begin())->dir && (*dn_set.begin())->dir->parent_inode); | |
84 | (*dn_set.begin())->dir->parent_inode->make_long_path(p); | |
85 | p.push_dentry((*dn_set.begin())->name); | |
86 | } else if (snapdir_parent) { | |
87 | snapdir_parent->make_nosnap_relative_path(p); | |
88 | string empty; | |
89 | p.push_dentry(empty); | |
90 | } else | |
91 | p = filepath(ino); | |
92 | } | |
93 | ||
94 | /* | |
95 | * make a filepath suitable for an mds request: | |
96 | * - if we are non-snapped/live, the ino is sufficient, e.g. #1234 | |
97 | * - if we are snapped, make filepath relative to first non-snapped parent. | |
98 | */ | |
99 | void Inode::make_nosnap_relative_path(filepath& p) | |
100 | { | |
101 | if (snapid == CEPH_NOSNAP) { | |
102 | p = filepath(ino); | |
103 | } else if (snapdir_parent) { | |
104 | snapdir_parent->make_nosnap_relative_path(p); | |
105 | string empty; | |
106 | p.push_dentry(empty); | |
107 | } else if (!dn_set.empty()) { | |
108 | assert((*dn_set.begin())->dir && (*dn_set.begin())->dir->parent_inode); | |
109 | (*dn_set.begin())->dir->parent_inode->make_nosnap_relative_path(p); | |
110 | p.push_dentry((*dn_set.begin())->name); | |
111 | } else { | |
112 | p = filepath(ino); | |
113 | } | |
114 | } | |
115 | ||
116 | void Inode::get_open_ref(int mode) | |
117 | { | |
118 | open_by_mode[mode]++; | |
119 | } | |
120 | ||
121 | bool Inode::put_open_ref(int mode) | |
122 | { | |
123 | //cout << "open_by_mode[" << mode << "] " << open_by_mode[mode] << " -> " << (open_by_mode[mode]-1) << std::endl; | |
124 | if (--open_by_mode[mode] == 0) | |
125 | return true; | |
126 | return false; | |
127 | } | |
128 | ||
129 | void Inode::get_cap_ref(int cap) | |
130 | { | |
131 | int n = 0; | |
132 | while (cap) { | |
133 | if (cap & 1) { | |
134 | int c = 1 << n; | |
135 | cap_refs[c]++; | |
136 | //cout << "inode " << *this << " get " << cap_string(c) << " " << (cap_refs[c]-1) << " -> " << cap_refs[c] << std::endl; | |
137 | } | |
138 | cap >>= 1; | |
139 | n++; | |
140 | } | |
141 | } | |
142 | ||
143 | int Inode::put_cap_ref(int cap) | |
144 | { | |
145 | // if cap is always a single bit (which it seems to be) | |
146 | // all this logic is equivalent to: | |
147 | // if (--cap_refs[c]) return false; else return true; | |
148 | int last = 0; | |
149 | int n = 0; | |
150 | while (cap) { | |
151 | if (cap & 1) { | |
152 | int c = 1 << n; | |
153 | if (cap_refs[c] <= 0) { | |
154 | lderr(client->cct) << "put_cap_ref " << ccap_string(c) << " went negative on " << *this << dendl; | |
155 | assert(cap_refs[c] > 0); | |
156 | } | |
157 | if (--cap_refs[c] == 0) | |
158 | last |= c; | |
159 | //cout << "inode " << *this << " put " << cap_string(c) << " " << (cap_refs[c]+1) << " -> " << cap_refs[c] << std::endl; | |
160 | } | |
161 | cap >>= 1; | |
162 | n++; | |
163 | } | |
164 | return last; | |
165 | } | |
166 | ||
167 | bool Inode::is_any_caps() | |
168 | { | |
169 | return !caps.empty() || snap_caps; | |
170 | } | |
171 | ||
172 | bool Inode::cap_is_valid(Cap* cap) const | |
173 | { | |
174 | /*cout << "cap_gen " << cap->session-> cap_gen << std::endl | |
175 | << "session gen " << cap->gen << std::endl | |
176 | << "cap expire " << cap->session->cap_ttl << std::endl | |
177 | << "cur time " << ceph_clock_now(cct) << std::endl;*/ | |
178 | if ((cap->session->cap_gen <= cap->gen) | |
179 | && (ceph_clock_now() < cap->session->cap_ttl)) { | |
180 | return true; | |
181 | } | |
182 | return false; | |
183 | } | |
184 | ||
185 | int Inode::caps_issued(int *implemented) const | |
186 | { | |
187 | int c = snap_caps; | |
188 | int i = 0; | |
189 | for (map<mds_rank_t,Cap*>::const_iterator it = caps.begin(); | |
190 | it != caps.end(); | |
191 | ++it) | |
192 | if (cap_is_valid(it->second)) { | |
193 | c |= it->second->issued; | |
194 | i |= it->second->implemented; | |
195 | } | |
196 | if (implemented) | |
197 | *implemented = i; | |
198 | return c; | |
199 | } | |
200 | ||
201 | void Inode::touch_cap(Cap *cap) | |
202 | { | |
203 | // move to back of LRU | |
204 | cap->session->caps.push_back(&cap->cap_item); | |
205 | } | |
206 | ||
207 | void Inode::try_touch_cap(mds_rank_t mds) | |
208 | { | |
209 | if (caps.count(mds)) | |
210 | touch_cap(caps[mds]); | |
211 | } | |
212 | ||
213 | bool Inode::caps_issued_mask(unsigned mask) | |
214 | { | |
215 | int c = snap_caps; | |
216 | if ((c & mask) == mask) | |
217 | return true; | |
218 | // prefer auth cap | |
219 | if (auth_cap && | |
220 | cap_is_valid(auth_cap) && | |
221 | (auth_cap->issued & mask) == mask) { | |
222 | touch_cap(auth_cap); | |
223 | return true; | |
224 | } | |
225 | // try any cap | |
226 | for (map<mds_rank_t,Cap*>::iterator it = caps.begin(); | |
227 | it != caps.end(); | |
228 | ++it) { | |
229 | if (cap_is_valid(it->second)) { | |
230 | if ((it->second->issued & mask) == mask) { | |
231 | touch_cap(it->second); | |
232 | return true; | |
233 | } | |
234 | c |= it->second->issued; | |
235 | } | |
236 | } | |
237 | if ((c & mask) == mask) { | |
238 | // bah.. touch them all | |
239 | for (map<mds_rank_t,Cap*>::iterator it = caps.begin(); | |
240 | it != caps.end(); | |
241 | ++it) | |
242 | touch_cap(it->second); | |
243 | return true; | |
244 | } | |
245 | return false; | |
246 | } | |
247 | ||
248 | int Inode::caps_used() | |
249 | { | |
250 | int w = 0; | |
251 | for (map<int,int>::iterator p = cap_refs.begin(); | |
252 | p != cap_refs.end(); | |
253 | ++p) | |
254 | if (p->second) | |
255 | w |= p->first; | |
256 | return w; | |
257 | } | |
258 | ||
259 | int Inode::caps_file_wanted() | |
260 | { | |
261 | int want = 0; | |
262 | for (map<int,int>::iterator p = open_by_mode.begin(); | |
263 | p != open_by_mode.end(); | |
264 | ++p) | |
265 | if (p->second) | |
266 | want |= ceph_caps_for_mode(p->first); | |
267 | return want; | |
268 | } | |
269 | ||
270 | int Inode::caps_wanted() | |
271 | { | |
272 | int want = caps_file_wanted() | caps_used(); | |
273 | if (want & CEPH_CAP_FILE_BUFFER) | |
274 | want |= CEPH_CAP_FILE_EXCL; | |
275 | return want; | |
276 | } | |
277 | ||
278 | int Inode::caps_mds_wanted() | |
279 | { | |
280 | int want = 0; | |
281 | for (auto it = caps.begin(); it != caps.end(); ++it) | |
282 | want |= it->second->wanted; | |
283 | return want; | |
284 | } | |
285 | ||
286 | int Inode::caps_dirty() | |
287 | { | |
288 | return dirty_caps | flushing_caps; | |
289 | } | |
290 | ||
291 | const UserPerm* Inode::get_best_perms() | |
292 | { | |
293 | const UserPerm *perms = NULL; | |
294 | for (const auto ci : caps) { | |
295 | const UserPerm& iperm = ci.second->latest_perms; | |
296 | if (!perms) { // we don't have any, take what's present | |
297 | perms = &iperm; | |
298 | } else if (iperm.uid() == uid) { | |
299 | if (iperm.gid() == gid) { // we have the best possible, return | |
300 | return &iperm; | |
301 | } | |
302 | if (perms->uid() != uid) { // take uid > gid every time | |
303 | perms = &iperm; | |
304 | } | |
305 | } else if (perms->uid() != uid && iperm.gid() == gid) { | |
306 | perms = &iperm; // a matching gid is better than nothing | |
307 | } | |
308 | } | |
309 | return perms; | |
310 | } | |
311 | ||
312 | bool Inode::have_valid_size() | |
313 | { | |
314 | // RD+RDCACHE or WR+WRBUFFER => valid size | |
315 | if (caps_issued() & (CEPH_CAP_FILE_SHARED | CEPH_CAP_FILE_EXCL)) | |
316 | return true; | |
317 | return false; | |
318 | } | |
319 | ||
320 | // open Dir for an inode. if it's not open, allocated it (and pin dentry in memory). | |
321 | Dir *Inode::open_dir() | |
322 | { | |
323 | if (!dir) { | |
324 | dir = new Dir(this); | |
325 | lsubdout(client->cct, client, 15) << "open_dir " << dir << " on " << this << dendl; | |
326 | assert(dn_set.size() < 2); // dirs can't be hard-linked | |
327 | if (!dn_set.empty()) | |
328 | (*dn_set.begin())->get(); // pin dentry | |
329 | get(); // pin inode | |
330 | } | |
331 | return dir; | |
332 | } | |
333 | ||
334 | bool Inode::check_mode(const UserPerm& perms, unsigned want) | |
335 | { | |
336 | if (uid == perms.uid()) { | |
337 | // if uid is owner, owner entry determines access | |
338 | want = want << 6; | |
339 | } else if (perms.gid_in_groups(gid)) { | |
340 | // if a gid or sgid matches the owning group, group entry determines access | |
341 | want = want << 3; | |
342 | } | |
343 | ||
344 | return (mode & want) == want; | |
345 | } | |
346 | ||
347 | void Inode::get() { | |
348 | _ref++; | |
349 | lsubdout(client->cct, client, 15) << "inode.get on " << this << " " << ino << '.' << snapid | |
350 | << " now " << _ref << dendl; | |
351 | } | |
352 | ||
353 | //private method to put a reference; see Client::put_inode() | |
354 | int Inode::_put(int n) { | |
355 | _ref -= n; | |
356 | lsubdout(client->cct, client, 15) << "inode.put on " << this << " " << ino << '.' << snapid | |
357 | << " now " << _ref << dendl; | |
358 | assert(_ref >= 0); | |
359 | return _ref; | |
360 | } | |
361 | ||
362 | ||
363 | void Inode::dump(Formatter *f) const | |
364 | { | |
365 | f->dump_stream("ino") << ino; | |
366 | f->dump_stream("snapid") << snapid; | |
367 | if (rdev) | |
368 | f->dump_unsigned("rdev", rdev); | |
369 | f->dump_stream("ctime") << ctime; | |
370 | f->dump_stream("btime") << btime; | |
371 | f->dump_stream("mode") << '0' << std::oct << mode << std::dec; | |
372 | f->dump_unsigned("uid", uid); | |
373 | f->dump_unsigned("gid", gid); | |
31f18b77 | 374 | f->dump_int("nlink", nlink); |
7c673cae | 375 | |
31f18b77 FG |
376 | f->dump_unsigned("size", size); |
377 | f->dump_unsigned("max_size", max_size); | |
378 | f->dump_unsigned("truncate_seq", truncate_seq); | |
379 | f->dump_unsigned("truncate_size", truncate_size); | |
7c673cae FG |
380 | f->dump_stream("mtime") << mtime; |
381 | f->dump_stream("atime") << atime; | |
31f18b77 FG |
382 | f->dump_unsigned("time_warp_seq", time_warp_seq); |
383 | f->dump_unsigned("change_attr", change_attr); | |
7c673cae FG |
384 | |
385 | f->dump_object("layout", layout); | |
386 | if (is_dir()) { | |
387 | f->open_object_section("dir_layout"); | |
388 | ::dump(dir_layout, f); | |
389 | f->close_section(); | |
390 | ||
391 | f->dump_bool("complete", flags & I_COMPLETE); | |
392 | f->dump_bool("ordered", flags & I_DIR_ORDERED); | |
393 | ||
394 | /* FIXME when wip-mds-encoding is merged *** | |
395 | f->open_object_section("dir_stat"); | |
396 | dirstat.dump(f); | |
397 | f->close_section(); | |
398 | ||
399 | f->open_object_section("rstat"); | |
400 | rstat.dump(f); | |
401 | f->close_section(); | |
402 | */ | |
403 | } | |
404 | ||
405 | f->dump_unsigned("version", version); | |
406 | f->dump_unsigned("xattr_version", xattr_version); | |
407 | f->dump_unsigned("flags", flags); | |
408 | ||
409 | if (is_dir()) { | |
410 | if (!dir_contacts.empty()) { | |
411 | f->open_object_section("dir_contants"); | |
412 | for (set<int>::iterator p = dir_contacts.begin(); p != dir_contacts.end(); ++p) | |
413 | f->dump_int("mds", *p); | |
414 | f->close_section(); | |
415 | } | |
416 | f->dump_int("dir_hashed", (int)dir_hashed); | |
417 | f->dump_int("dir_replicated", (int)dir_replicated); | |
418 | } | |
419 | ||
420 | f->open_array_section("caps"); | |
421 | for (map<mds_rank_t,Cap*>::const_iterator p = caps.begin(); p != caps.end(); ++p) { | |
422 | f->open_object_section("cap"); | |
423 | f->dump_int("mds", p->first); | |
424 | if (p->second == auth_cap) | |
425 | f->dump_int("auth", 1); | |
426 | p->second->dump(f); | |
427 | f->close_section(); | |
428 | } | |
429 | f->close_section(); | |
430 | if (auth_cap) | |
431 | f->dump_int("auth_cap", auth_cap->session->mds_num); | |
432 | ||
433 | f->dump_stream("dirty_caps") << ccap_string(dirty_caps); | |
434 | if (flushing_caps) { | |
435 | f->dump_stream("flushings_caps") << ccap_string(flushing_caps); | |
436 | f->open_object_section("flushing_cap_tid"); | |
437 | for (map<ceph_tid_t, int>::const_iterator p = flushing_cap_tids.begin(); | |
438 | p != flushing_cap_tids.end(); | |
439 | ++p) { | |
440 | string n(ccap_string(p->second)); | |
441 | f->dump_unsigned(n.c_str(), p->first); | |
442 | } | |
443 | f->close_section(); | |
444 | } | |
445 | f->dump_int("shared_gen", shared_gen); | |
446 | f->dump_int("cache_gen", cache_gen); | |
447 | if (snap_caps) { | |
448 | f->dump_int("snap_caps", snap_caps); | |
449 | f->dump_int("snap_cap_refs", snap_cap_refs); | |
450 | } | |
451 | ||
452 | f->dump_stream("hold_caps_until") << hold_caps_until; | |
453 | ||
454 | if (snaprealm) { | |
455 | f->open_object_section("snaprealm"); | |
456 | snaprealm->dump(f); | |
457 | f->close_section(); | |
458 | } | |
459 | if (!cap_snaps.empty()) { | |
460 | for (const auto &p : cap_snaps) { | |
461 | f->open_object_section("cap_snap"); | |
462 | f->dump_stream("follows") << p.first; | |
463 | p.second.dump(f); | |
464 | f->close_section(); | |
465 | } | |
466 | } | |
467 | ||
468 | // open | |
469 | if (!open_by_mode.empty()) { | |
470 | f->open_array_section("open_by_mode"); | |
471 | for (map<int,int>::const_iterator p = open_by_mode.begin(); p != open_by_mode.end(); ++p) { | |
472 | f->open_object_section("ref"); | |
31f18b77 FG |
473 | f->dump_int("mode", p->first); |
474 | f->dump_int("refs", p->second); | |
7c673cae FG |
475 | f->close_section(); |
476 | } | |
477 | f->close_section(); | |
478 | } | |
479 | if (!cap_refs.empty()) { | |
480 | f->open_array_section("cap_refs"); | |
481 | for (map<int,int>::const_iterator p = cap_refs.begin(); p != cap_refs.end(); ++p) { | |
482 | f->open_object_section("cap_ref"); | |
483 | f->dump_stream("cap") << ccap_string(p->first); | |
484 | f->dump_int("refs", p->second); | |
485 | f->close_section(); | |
486 | } | |
487 | f->close_section(); | |
488 | } | |
489 | ||
490 | f->dump_unsigned("reported_size", reported_size); | |
491 | if (wanted_max_size != max_size) | |
492 | f->dump_unsigned("wanted_max_size", wanted_max_size); | |
493 | if (requested_max_size != max_size) | |
494 | f->dump_unsigned("requested_max_size", requested_max_size); | |
495 | ||
496 | f->dump_int("ref", _ref); | |
497 | f->dump_int("ll_ref", ll_ref); | |
498 | ||
499 | if (!dn_set.empty()) { | |
500 | f->open_array_section("parents"); | |
501 | for (set<Dentry*>::const_iterator p = dn_set.begin(); p != dn_set.end(); ++p) { | |
502 | f->open_object_section("dentry"); | |
503 | f->dump_stream("dir_ino") << (*p)->dir->parent_inode->ino; | |
504 | f->dump_string("name", (*p)->name); | |
505 | f->close_section(); | |
506 | } | |
507 | f->close_section(); | |
508 | } | |
509 | } | |
510 | ||
511 | void Cap::dump(Formatter *f) const | |
512 | { | |
513 | f->dump_int("mds", session->mds_num); | |
514 | f->dump_stream("ino") << inode->ino; | |
515 | f->dump_unsigned("cap_id", cap_id); | |
516 | f->dump_stream("issued") << ccap_string(issued); | |
517 | if (implemented != issued) | |
518 | f->dump_stream("implemented") << ccap_string(implemented); | |
519 | f->dump_stream("wanted") << ccap_string(wanted); | |
520 | f->dump_unsigned("seq", seq); | |
521 | f->dump_unsigned("issue_seq", issue_seq); | |
522 | f->dump_unsigned("mseq", mseq); | |
523 | f->dump_unsigned("gen", gen); | |
524 | } | |
525 | ||
526 | void CapSnap::dump(Formatter *f) const | |
527 | { | |
528 | f->dump_stream("ino") << in->ino; | |
529 | f->dump_stream("issued") << ccap_string(issued); | |
530 | f->dump_stream("dirty") << ccap_string(dirty); | |
531 | f->dump_unsigned("size", size); | |
532 | f->dump_stream("ctime") << ctime; | |
533 | f->dump_stream("mtime") << mtime; | |
534 | f->dump_stream("atime") << atime; | |
535 | f->dump_int("time_warp_seq", time_warp_seq); | |
536 | f->dump_stream("mode") << '0' << std::oct << mode << std::dec; | |
537 | f->dump_unsigned("uid", uid); | |
538 | f->dump_unsigned("gid", gid); | |
539 | if (!xattrs.empty()) { | |
540 | f->open_object_section("xattr_lens"); | |
541 | for (map<string,bufferptr>::const_iterator p = xattrs.begin(); p != xattrs.end(); ++p) | |
542 | f->dump_int(p->first.c_str(), p->second.length()); | |
543 | f->close_section(); | |
544 | } | |
545 | f->dump_unsigned("xattr_version", xattr_version); | |
546 | f->dump_int("writing", (int)writing); | |
547 | f->dump_int("dirty_data", (int)dirty_data); | |
548 | f->dump_unsigned("flush_tid", flush_tid); | |
549 | } | |
550 | ||
551 | void Inode::set_async_err(int r) | |
552 | { | |
553 | for (const auto &fh : fhs) { | |
554 | fh->async_err = r; | |
555 | } | |
556 | } | |
557 |