1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 * Ceph - scalable distributed file system
6 * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
8 * This is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License version 2.1, as published by the Free Software
11 * Foundation. See file COPYING.
20 #include "include/counter.h"
21 #include "include/types.h"
22 #include "include/buffer_fwd.h"
23 #include "common/bloom_filter.hpp"
24 #include "common/config.h"
25 #include "common/DecayCounter.h"
27 #include "MDSCacheObject.h"
42 struct ObjectOperation
;
44 ostream
& operator<<(ostream
& out
, const class CDir
& dir
);
45 class CDir
: public MDSCacheObject
, public Counter
<CDir
> {
46 friend ostream
& operator<<(ostream
& out
, const class CDir
& dir
);
50 static const int PIN_DNWAITER
= 1;
51 static const int PIN_INOWAITER
= 2;
52 static const int PIN_CHILD
= 3;
53 static const int PIN_FROZEN
= 4;
54 static const int PIN_SUBTREE
= 5;
55 static const int PIN_IMPORTING
= 7;
56 static const int PIN_IMPORTBOUND
= 9;
57 static const int PIN_EXPORTBOUND
= 10;
58 static const int PIN_STICKY
= 11;
59 static const int PIN_SUBTREETEMP
= 12; // used by MDCache::trim_non_auth()
60 const char *pin_name(int p
) const override
{
62 case PIN_DNWAITER
: return "dnwaiter";
63 case PIN_INOWAITER
: return "inowaiter";
64 case PIN_CHILD
: return "child";
65 case PIN_FROZEN
: return "frozen";
66 case PIN_SUBTREE
: return "subtree";
67 case PIN_IMPORTING
: return "importing";
68 case PIN_IMPORTBOUND
: return "importbound";
69 case PIN_EXPORTBOUND
: return "exportbound";
70 case PIN_STICKY
: return "sticky";
71 case PIN_SUBTREETEMP
: return "subtreetemp";
72 default: return generic_pin_name(p
);
77 static const unsigned STATE_COMPLETE
= (1<< 1); // the complete contents are in cache
78 static const unsigned STATE_FROZENTREE
= (1<< 2); // root of tree (bounded by exports)
79 static const unsigned STATE_FREEZINGTREE
= (1<< 3); // in process of freezing
80 static const unsigned STATE_FROZENDIR
= (1<< 4);
81 static const unsigned STATE_FREEZINGDIR
= (1<< 5);
82 static const unsigned STATE_COMMITTING
= (1<< 6); // mid-commit
83 static const unsigned STATE_FETCHING
= (1<< 7); // currenting fetching
84 static const unsigned STATE_CREATING
= (1<< 8);
85 static const unsigned STATE_IMPORTBOUND
= (1<<10);
86 static const unsigned STATE_EXPORTBOUND
= (1<<11);
87 static const unsigned STATE_EXPORTING
= (1<<12);
88 static const unsigned STATE_IMPORTING
= (1<<13);
89 static const unsigned STATE_FRAGMENTING
= (1<<14);
90 static const unsigned STATE_STICKY
= (1<<15); // sticky pin due to inode stickydirs
91 static const unsigned STATE_DNPINNEDFRAG
= (1<<16); // dir is refragmenting
92 static const unsigned STATE_ASSIMRSTAT
= (1<<17); // assimilating inode->frag rstats
93 static const unsigned STATE_DIRTYDFT
= (1<<18); // dirty dirfragtree
94 static const unsigned STATE_BADFRAG
= (1<<19); // bad dirfrag
95 static const unsigned STATE_AUXSUBTREE
= (1<<20); // no subtree merge
98 static const unsigned STATE_CLEAN
= 0;
99 static const unsigned STATE_INITIAL
= 0;
101 // these state bits are preserved by an import/export
102 // ...except if the directory is hashed, in which case none of them are!
103 static const unsigned MASK_STATE_EXPORTED
=
104 (STATE_COMPLETE
|STATE_DIRTY
|STATE_DIRTYDFT
|STATE_BADFRAG
);
105 static const unsigned MASK_STATE_IMPORT_KEPT
=
108 |STATE_IMPORTBOUND
|STATE_EXPORTBOUND
111 static const unsigned MASK_STATE_EXPORT_KEPT
=
113 |STATE_IMPORTBOUND
|STATE_EXPORTBOUND
117 static const unsigned MASK_STATE_FRAGMENT_KEPT
=
125 static const int REP_NONE
= 0;
126 static const int REP_ALL
= 1;
127 static const int REP_LIST
= 2;
130 static const unsigned EXPORT_NONCE
= 1;
134 static const uint64_t WAIT_DENTRY
= (1<<0); // wait for item to be in cache
135 static const uint64_t WAIT_COMPLETE
= (1<<1); // wait for complete dir contents
136 static const uint64_t WAIT_FROZEN
= (1<<2); // auth pins removed
137 static const uint64_t WAIT_CREATED
= (1<<3); // new dirfrag is logged
139 static const int WAIT_DNLOCK_OFFSET
= 4;
141 static const uint64_t WAIT_ANY_MASK
= (uint64_t)(-1);
142 static const uint64_t WAIT_ATFREEZEROOT
= (WAIT_UNFREEZE
);
143 static const uint64_t WAIT_ATSUBTREEROOT
= (WAIT_SINGLEAUTH
);
152 CInode
*inode
; // my inode
153 frag_t frag
; // my frag
155 bool is_lt(const MDSCacheObject
*r
) const override
{
156 return dirfrag() < (static_cast<const CDir
*>(r
))->dirfrag();
161 compact_map
<snapid_t
,old_rstat_t
> dirty_old_rstat
; // [value.first,key]
163 // my inodes with dirty rstat data
164 elist
<CInode
*> dirty_rstat_inodes
;
166 void resync_accounted_fragstat();
167 void resync_accounted_rstat();
168 void assimilate_dirty_rstat_inodes();
169 void assimilate_dirty_rstat_inodes_finish(MutationRef
& mut
, EMetaBlob
*blob
);
172 version_t projected_version
;
173 std::list
<fnode_t
*> projected_fnode
;
176 elist
<CDir
*>::item item_dirty
, item_new
;
180 version_t
get_version() const { return fnode
.version
; }
181 void set_version(version_t v
) {
182 assert(projected_fnode
.empty());
183 projected_version
= fnode
.version
= v
;
185 version_t
get_projected_version() const { return projected_version
; }
187 const fnode_t
*get_projected_fnode() const {
188 if (projected_fnode
.empty())
191 return projected_fnode
.back();
194 fnode_t
*get_projected_fnode() {
195 if (projected_fnode
.empty())
198 return projected_fnode
.back();
200 fnode_t
*project_fnode();
202 void pop_and_dirty_projected_fnode(LogSegment
*ls
);
203 bool is_projected() const { return !projected_fnode
.empty(); }
204 version_t
pre_dirty(version_t min
=0);
205 void _mark_dirty(LogSegment
*ls
);
206 void _set_dirty_flag() {
207 if (!state_test(STATE_DIRTY
)) {
208 state_set(STATE_DIRTY
);
212 void mark_dirty(version_t pv
, LogSegment
*ls
);
215 bool is_new() { return item_new
.is_on_list(); }
216 void mark_new(LogSegment
*ls
);
218 bool is_bad() { return state_test(STATE_BADFRAG
); }
220 void log_mark_dirty();
223 typedef std::map
<dentry_key_t
, CDentry
*> map_t
;
227 /// inodes we contain with dirty scrub stamps
228 map
<dentry_key_t
,CInode
*> dirty_scrub_stamps
; // TODO: make use of this!
229 struct scrub_stamps
{
232 scrub_stamps() : version(0) {}
233 void operator=(const scrub_stamps
&o
) {
239 scrub_stamps recursive_start
; // when we last started a recursive scrub
240 scrub_stamps last_recursive
; // when we last finished a recursive scrub
241 scrub_stamps last_local
; // when we last did a local scrub
243 bool directory_scrubbing
; /// safety check
244 bool need_scrub_local
;
245 bool last_scrub_dirty
; /// is scrub info dirty or is it flushed to fnode?
246 bool pending_scrub_error
;
248 /// these are lists of children in each stage of scrubbing
249 set
<dentry_key_t
> directories_to_scrub
;
250 set
<dentry_key_t
> directories_scrubbing
;
251 set
<dentry_key_t
> directories_scrubbed
;
252 set
<dentry_key_t
> others_to_scrub
;
253 set
<dentry_key_t
> others_scrubbing
;
254 set
<dentry_key_t
> others_scrubbed
;
256 ScrubHeaderRefConst header
;
259 directory_scrubbing(false),
260 need_scrub_local(false),
261 last_scrub_dirty(false),
262 pending_scrub_error(false) {}
265 * Call to start this CDir on a new scrub.
266 * @pre It is not currently scrubbing
267 * @pre The CDir is marked complete.
268 * @post It has set up its internal scrubbing state.
270 void scrub_initialize(const ScrubHeaderRefConst
& header
);
272 * Get the next dentry to scrub. Gives you a CDentry* and its meaning. This
273 * function will give you all directory-representing dentries before any
275 * 0: success, you should scrub this CDentry right now
276 * EAGAIN: is currently fetching the next CDentry into memory for you.
277 * It will activate your callback when done; try again when it does!
278 * ENOENT: there are no remaining dentries to scrub
279 * <0: There was an unexpected error
281 * @param cb An MDSInternalContext which will be activated only if
282 * we return EAGAIN via rcode, or else ignored
283 * @param dnout CDentry * which you should next scrub, or NULL
284 * @returns a value as described above
286 int scrub_dentry_next(MDSInternalContext
*cb
, CDentry
**dnout
);
288 * Get the currently scrubbing dentries. When returned, the passed-in
289 * list will be filled with all CDentry * which have been returned
290 * from scrub_dentry_next() but not sent back via scrub_dentry_finished().
292 void scrub_dentries_scrubbing(list
<CDentry
*> *out_dentries
);
294 * Report to the CDir that a CDentry has been scrubbed. Call this
295 * for every CDentry returned from scrub_dentry_next().
296 * @param dn The CDentry which has been scrubbed.
298 void scrub_dentry_finished(CDentry
*dn
);
300 * Call this once all CDentries have been scrubbed, according to
301 * scrub_dentry_next's listing. It finalizes the scrub statistics.
303 void scrub_finished();
305 * Tell the CDir to do a local scrub of itself.
306 * @pre The CDir is_complete().
307 * @returns true if the rstats and directory contents match, false otherwise.
312 * Create a scrub_info_t struct for the scrub_infop pointer.
314 void scrub_info_create() const;
316 * Delete the scrub_infop if it's not got any useful data.
318 void scrub_maybe_delete_info();
320 * Check the given set (presumably one of those in scrub_info_t) for the
321 * next key to scrub and look it up (or fail!).
323 int _next_dentry_on_set(set
<dentry_key_t
>& dns
, bool missing_okay
,
324 MDSInternalContext
*cb
, CDentry
**dnout
);
328 std::unique_ptr
<scrub_info_t
> scrub_infop
;
330 // contents of this directory
331 map_t items
; // non-null AND null
332 unsigned num_head_items
;
333 unsigned num_head_null
;
334 unsigned num_snap_items
;
335 unsigned num_snap_null
;
340 version_t committing_version
;
341 version_t committed_version
;
343 compact_set
<string
> stale_items
;
345 // lock nesting, freeze
346 static int num_frozen_trees
;
347 static int num_freezing_trees
;
352 // cache control (defined for authority; hints for replicas)
354 compact_set
<__s32
> dir_rep_by
; // if dir_rep == REP_LIST
357 dirfrag_load_vec_t pop_me
;
358 dirfrag_load_vec_t pop_nested
;
359 dirfrag_load_vec_t pop_auth_subtree
;
360 dirfrag_load_vec_t pop_auth_subtree_nested
;
362 utime_t last_popularity_sample
;
364 load_spread_t pop_spread
;
366 // and to provide density
367 int num_dentries_nested
;
368 int num_dentries_auth_subtree
;
369 int num_dentries_auth_subtree_nested
;
373 friend class Migrator
;
375 friend class MDCache
;
376 friend class MDiscover
;
377 friend class MDBalancer
;
379 friend class CDirDiscover
;
380 friend class CDirExport
;
381 friend class C_IO_Dir_TMAP_Fetched
;
382 friend class C_IO_Dir_OMAP_Fetched
;
383 friend class C_IO_Dir_OMAP_FetchedMore
;
384 friend class C_IO_Dir_Committed
;
386 std::unique_ptr
<bloom_filter
> bloom
;
387 /* If you set up the bloom filter, you must keep it accurate!
388 * It's deleted when you mark_complete() and is deliberately not serialized.*/
391 CDir(CInode
*in
, frag_t fg
, MDCache
*mdcache
, bool auth
);
393 const scrub_info_t
*scrub_info() const {
397 return scrub_infop
.get();
402 inodeno_t
ino() const { return inode
->ino(); } // deprecate me?
403 frag_t
get_frag() const { return frag
; }
404 dirfrag_t
dirfrag() const { return dirfrag_t(inode
->ino(), frag
); }
406 CInode
*get_inode() { return inode
; }
407 const CInode
*get_inode() const { return inode
; }
408 CDir
*get_parent_dir() { return inode
->get_parent_dir(); }
410 map_t::iterator
begin() { return items
.begin(); }
411 map_t::iterator
end() { return items
.end(); }
412 map_t::iterator
lower_bound(dentry_key_t key
) { return items
.lower_bound(key
); }
414 unsigned get_num_head_items() const { return num_head_items
; }
415 unsigned get_num_head_null() const { return num_head_null
; }
416 unsigned get_num_snap_items() const { return num_snap_items
; }
417 unsigned get_num_snap_null() const { return num_snap_null
; }
418 unsigned get_num_any() const { return num_head_items
+ num_head_null
+ num_snap_items
+ num_snap_null
; }
420 bool check_rstats(bool scrub
=false);
422 void inc_num_dirty() { num_dirty
++; }
423 void dec_num_dirty() {
424 assert(num_dirty
> 0);
427 int get_num_dirty() const {
431 int64_t get_frag_size() const {
432 return get_projected_fnode()->fragstat
.size();
435 // -- dentries and inodes --
437 CDentry
* lookup_exact_snap(const std::string
& dname
, snapid_t last
);
438 CDentry
* lookup(const std::string
& n
, snapid_t snap
=CEPH_NOSNAP
);
439 CDentry
* lookup(const char *n
, snapid_t snap
=CEPH_NOSNAP
) {
440 return lookup(std::string(n
), snap
);
443 CDentry
* add_null_dentry(const std::string
& dname
,
444 snapid_t first
=2, snapid_t last
=CEPH_NOSNAP
);
445 CDentry
* add_primary_dentry(const std::string
& dname
, CInode
*in
,
446 snapid_t first
=2, snapid_t last
=CEPH_NOSNAP
);
447 CDentry
* add_remote_dentry(const std::string
& dname
, inodeno_t ino
, unsigned char d_type
,
448 snapid_t first
=2, snapid_t last
=CEPH_NOSNAP
);
449 void remove_dentry( CDentry
*dn
); // delete dentry
450 void link_remote_inode( CDentry
*dn
, inodeno_t ino
, unsigned char d_type
);
451 void link_remote_inode( CDentry
*dn
, CInode
*in
);
452 void link_primary_inode( CDentry
*dn
, CInode
*in
);
453 void unlink_inode( CDentry
*dn
);
454 void try_remove_unlinked_dn(CDentry
*dn
);
456 void add_to_bloom(CDentry
*dn
);
457 bool is_in_bloom(const std::string
& name
);
458 bool has_bloom() { return (bloom
? true : false); }
459 void remove_bloom() {
463 void link_inode_work( CDentry
*dn
, CInode
*in
);
464 void unlink_inode_work( CDentry
*dn
);
465 void remove_null_dentries();
466 void purge_stale_snap_data(const std::set
<snapid_t
>& snaps
);
468 void touch_dentries_bottom();
469 void try_remove_dentries_for_stray();
470 bool try_trim_snap_dentry(CDentry
*dn
, const std::set
<snapid_t
>& snaps
);
474 void split(int bits
, list
<CDir
*>& subs
, list
<MDSInternalContextBase
*>& waiters
, bool replay
);
475 void merge(list
<CDir
*>& subs
, list
<MDSInternalContextBase
*>& waiters
, bool replay
);
477 bool should_split() const {
478 return (int)get_frag_size() > g_conf
->mds_bal_split_size
;
480 bool should_split_fast() const;
481 bool should_merge() const {
482 return (int)get_frag_size() < g_conf
->mds_bal_merge_size
;
486 void prepare_new_fragment(bool replay
);
487 void prepare_old_fragment(bool replay
);
488 void steal_dentry(CDentry
*dn
); // from another dir. used by merge/split.
489 void finish_old_fragment(list
<MDSInternalContextBase
*>& waiters
, bool replay
);
490 void init_fragment_pins();
495 * normal: <parent,unknown> !subtree_root
496 * delegation: <mds,unknown> subtree_root
497 * ambiguous: <mds1,mds2> subtree_root
498 * <parent,mds2> subtree_root
500 mds_authority_t dir_auth
;
502 std::string
get_path() const;
505 mds_authority_t
authority() const override
;
506 mds_authority_t
get_dir_auth() const { return dir_auth
; }
507 void set_dir_auth(mds_authority_t a
);
508 void set_dir_auth(mds_rank_t a
) { set_dir_auth(mds_authority_t(a
, CDIR_AUTH_UNKNOWN
)); }
509 bool is_ambiguous_dir_auth() const {
510 return dir_auth
.second
!= CDIR_AUTH_UNKNOWN
;
512 bool is_full_dir_auth() const {
513 return is_auth() && !is_ambiguous_dir_auth();
515 bool is_full_dir_nonauth() const {
516 return !is_auth() && !is_ambiguous_dir_auth();
519 bool is_subtree_root() const {
520 return dir_auth
!= CDIR_AUTH_DEFAULT
;
523 bool contains(CDir
*x
); // true if we are x or an ancestor of x
526 // for giving to clients
527 void get_dist_spec(std::set
<mds_rank_t
>& ls
, mds_rank_t auth
) {
534 void encode_dirstat(bufferlist
& bl
, mds_rank_t whoami
) {
536 * note: encoding matches struct ceph_client_reply_dirfrag
538 frag_t frag
= get_frag();
540 std::set
<mds_rank_t
> dist
;
542 auth
= dir_auth
.first
;
544 get_dist_spec(dist
, whoami
);
551 void _encode_base(bufferlist
& bl
) {
554 ::encode(dir_rep
, bl
);
555 ::encode(dir_rep_by
, bl
);
557 void _decode_base(bufferlist::iterator
& p
) {
560 ::decode(dir_rep
, p
);
561 ::decode(dir_rep_by
, p
);
563 void encode_replica(mds_rank_t who
, bufferlist
& bl
) {
564 __u32 nonce
= add_replica(who
);
568 void decode_replica(bufferlist::iterator
& p
) {
571 replica_nonce
= nonce
;
578 bool is_complete() { return state
& STATE_COMPLETE
; }
579 bool is_exporting() { return state
& STATE_EXPORTING
; }
580 bool is_importing() { return state
& STATE_IMPORTING
; }
581 bool is_dirty_dft() { return state
& STATE_DIRTYDFT
; }
583 int get_dir_rep() const { return dir_rep
; }
584 bool is_rep() const {
585 if (dir_rep
== REP_NONE
) return false;
590 object_t
get_ondisk_object() {
591 return file_object_t(ino(), frag
);
593 void fetch(MDSInternalContextBase
*c
, bool ignore_authpinnability
=false);
594 void fetch(MDSInternalContextBase
*c
, const std::string
& want_dn
, bool ignore_authpinnability
=false);
595 void fetch(MDSInternalContextBase
*c
, const std::set
<dentry_key_t
>& keys
);
597 compact_set
<string
> wanted_items
;
599 void _omap_fetch(MDSInternalContextBase
*fin
, const std::set
<dentry_key_t
>& keys
);
600 void _omap_fetch_more(
601 bufferlist
& hdrbl
, std::map
<std::string
, bufferlist
>& omap
,
602 MDSInternalContextBase
*fin
);
603 CDentry
*_load_dentry(
604 const std::string
&key
,
605 const std::string
&dname
,
609 const std::set
<snapid_t
> *snaps
,
611 list
<CInode
*> *undef_inodes
);
614 * Mark this fragment as BADFRAG (common part of go_bad and go_bad_dentry)
619 * Go bad due to a damaged dentry (register with damagetable and go BADFRAG)
621 void go_bad_dentry(snapid_t last
, const std::string
&dname
);
624 * Go bad due to a damaged header (register with damagetable and go BADFRAG)
626 void go_bad(bool complete
);
628 void _omap_fetched(bufferlist
& hdrbl
, std::map
<std::string
, bufferlist
>& omap
,
629 bool complete
, int r
);
632 compact_map
<version_t
, std::list
<MDSInternalContextBase
*> > waiting_for_commit
;
633 void _commit(version_t want
, int op_prio
);
634 void _omap_commit(int op_prio
);
635 void _encode_dentry(CDentry
*dn
, bufferlist
& bl
, const std::set
<snapid_t
> *snaps
);
636 void _committed(int r
, version_t v
);
639 void wait_for_commit(Context
*c
, version_t v
=0);
641 void commit_to(version_t want
);
642 void commit(version_t want
, MDSInternalContextBase
*c
,
643 bool ignore_authpinnability
=false, int op_prio
=-1);
646 version_t
get_committing_version() const { return committing_version
; }
647 version_t
get_committed_version() const { return committed_version
; }
648 void set_committed_version(version_t v
) { committed_version
= v
; }
650 void mark_complete();
653 // -- reference counting --
654 void first_get() override
;
655 void last_put() override
;
657 void request_pin_get() {
658 if (request_pins
== 0) get(PIN_REQUEST
);
661 void request_pin_put() {
663 if (request_pins
== 0) put(PIN_REQUEST
);
668 compact_map
< string_snap_t
, std::list
<MDSInternalContextBase
*> > waiting_on_dentry
;
671 bool is_waiting_for_dentry(const std::string
& dname
, snapid_t snap
) {
672 return waiting_on_dentry
.count(string_snap_t(dname
, snap
));
674 void add_dentry_waiter(const std::string
& dentry
, snapid_t snap
, MDSInternalContextBase
*c
);
675 void take_dentry_waiting(const std::string
& dentry
, snapid_t first
, snapid_t last
, std::list
<MDSInternalContextBase
*>& ls
);
676 void take_sub_waiting(std::list
<MDSInternalContextBase
*>& ls
); // dentry or ino
678 void add_waiter(uint64_t mask
, MDSInternalContextBase
*c
) override
;
679 void take_waiting(uint64_t mask
, std::list
<MDSInternalContextBase
*>& ls
) override
; // may include dentry waiters
680 void finish_waiting(uint64_t mask
, int result
= 0); // ditto
683 // -- import/export --
684 void encode_export(bufferlist
& bl
);
685 void finish_export(utime_t now
);
686 void abort_export() {
687 put(PIN_TEMPEXPORTING
);
689 void decode_import(bufferlist::iterator
& blp
, utime_t now
, LogSegment
*ls
);
692 bool can_auth_pin() const override
{ return is_auth() && !(is_frozen() || is_freezing()); }
693 int get_cum_auth_pins() const { return auth_pins
+ nested_auth_pins
; }
694 int get_auth_pins() const { return auth_pins
; }
695 int get_nested_auth_pins() const { return nested_auth_pins
; }
696 int get_dir_auth_pins() const { return dir_auth_pins
; }
697 void auth_pin(void *who
) override
;
698 void auth_unpin(void *who
) override
;
700 void adjust_nested_auth_pins(int inc
, int dirinc
, void *by
);
701 void verify_fragstat();
706 void unfreeze_tree();
712 void maybe_finish_freeze();
714 bool is_freezing() const override
{ return is_freezing_tree() || is_freezing_dir(); }
715 bool is_freezing_tree() const;
716 bool is_freezing_tree_root() const { return state
& STATE_FREEZINGTREE
; }
717 bool is_freezing_dir() const { return state
& STATE_FREEZINGDIR
; }
719 bool is_frozen() const override
{ return is_frozen_dir() || is_frozen_tree(); }
720 bool is_frozen_tree() const;
721 bool is_frozen_tree_root() const { return state
& STATE_FROZENTREE
; }
722 bool is_frozen_dir() const { return state
& STATE_FROZENDIR
; }
724 bool is_freezeable(bool freezing
=false) const {
725 // no nested auth pins.
726 if ((auth_pins
-freezing
) > 0 || nested_auth_pins
> 0)
729 // inode must not be frozen.
730 if (!is_subtree_root() && inode
->is_frozen())
735 bool is_freezeable_dir(bool freezing
=false) const {
736 if ((auth_pins
-freezing
) > 0 || dir_auth_pins
> 0)
739 // if not subtree root, inode must not be frozen (tree--frozen_dir is okay).
740 if (!is_subtree_root() && inode
->is_frozen() && !inode
->is_frozen_dir())
746 CDir
*get_frozen_tree_root();
749 ostream
& print_db_line_prefix(ostream
& out
) override
;
750 void print(ostream
& out
) override
;
751 void dump(Formatter
*f
) const;