2018-06-01 19:29:47 +03:00
|
|
|
/*
|
2019-06-20 02:58:25 +03:00
|
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
2018-06-01 19:29:47 +03:00
|
|
|
*
|
2019-06-20 02:58:25 +03:00
|
|
|
* This software may be used and distributed according to the terms of the
|
|
|
|
* GNU General Public License version 2.
|
2018-06-01 19:29:47 +03:00
|
|
|
*/
|
2019-10-11 15:26:59 +03:00
|
|
|
|
2018-06-01 19:29:47 +03:00
|
|
|
#pragma once
|
2018-10-24 04:48:38 +03:00
|
|
|
#include <optional>
|
2020-09-02 22:13:21 +03:00
|
|
|
#include "eden/fs/inodes/InodeNumber.h"
|
2018-06-01 19:29:47 +03:00
|
|
|
#include "eden/fs/inodes/InodePtr.h"
|
|
|
|
#include "eden/fs/model/Hash.h"
|
2021-04-20 23:07:31 +03:00
|
|
|
#include "eden/fs/utils/CaseSensitivity.h"
|
2018-06-01 19:29:47 +03:00
|
|
|
#include "eden/fs/utils/DirType.h"
|
|
|
|
#include "eden/fs/utils/PathMap.h"
|
|
|
|
|
|
|
|
namespace facebook {
|
|
|
|
namespace eden {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Represents a directory entry.
|
|
|
|
*
|
2018-06-01 21:57:49 +03:00
|
|
|
* A directory entry has two independent state conditions:
|
2018-06-01 19:29:47 +03:00
|
|
|
*
|
2018-06-01 21:57:49 +03:00
|
|
|
* - An InodeBase object for the entry may or may not exist. If it does
|
|
|
|
* exist, it is the authoritative source of data for the entry. If not, the
|
|
|
|
* type of the entry can be retrieved, but to read or update its contents or
|
|
|
|
* inode metadata, the InodeBase must be loaded.
|
2018-06-01 19:29:47 +03:00
|
|
|
*
|
|
|
|
* - The child may or may not be materialized in the overlay.
|
|
|
|
* If the child contents are identical to an existing source control Tree
|
|
|
|
* or Blob then it does not need to be materialized, and the Entry may only
|
2018-06-01 21:57:49 +03:00
|
|
|
* contain the hash identifying the Tree/Blob. If the entry is materialized,
|
|
|
|
* no hash is set and the entry's materialized contents are available in the
|
|
|
|
* Overlay under the entry's inode number.
|
2018-06-01 19:29:47 +03:00
|
|
|
*/
|
|
|
|
class DirEntry {
|
|
|
|
public:
|
|
|
|
/**
|
|
|
|
* Create a hash for a non-materialized entry.
|
|
|
|
*/
|
|
|
|
DirEntry(mode_t m, InodeNumber number, Hash hash)
|
2018-06-01 21:57:49 +03:00
|
|
|
: initialMode_{m},
|
|
|
|
hasHash_{true},
|
|
|
|
hasInodePointer_{false},
|
|
|
|
hash_{hash},
|
|
|
|
inodeNumber_{number} {
|
2020-11-11 03:29:24 +03:00
|
|
|
XCHECK_EQ(m, m & 0x3fffffff);
|
|
|
|
XDCHECK(number.hasValue());
|
2018-06-01 19:29:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create a hash for a materialized entry.
|
|
|
|
*/
|
2018-06-01 21:57:49 +03:00
|
|
|
DirEntry(mode_t m, InodeNumber number)
|
|
|
|
: initialMode_{m},
|
|
|
|
hasHash_{false},
|
|
|
|
hasInodePointer_{false},
|
|
|
|
inodeNumber_{number} {
|
2020-11-11 03:29:24 +03:00
|
|
|
XCHECK_EQ(m, m & 0x3fffffff);
|
|
|
|
XDCHECK(number.hasValue());
|
2018-06-01 19:29:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
DirEntry(DirEntry&& e) = default;
|
|
|
|
DirEntry& operator=(DirEntry&& e) = default;
|
|
|
|
DirEntry(const DirEntry& e) = delete;
|
|
|
|
DirEntry& operator=(const DirEntry& e) = delete;
|
|
|
|
|
|
|
|
bool isMaterialized() const {
|
|
|
|
// TODO: In the future we should probably only allow callers to invoke
|
|
|
|
// this method when inode is not set. If inode is set it should be the
|
|
|
|
// authoritative source of data.
|
|
|
|
return !hasHash_;
|
|
|
|
}
|
|
|
|
|
|
|
|
Hash getHash() const {
|
|
|
|
// TODO: In the future we should probably only allow callers to invoke
|
|
|
|
// this method when inode is not set. If inode is set it should be the
|
|
|
|
// authoritative source of data.
|
2020-11-11 03:29:24 +03:00
|
|
|
XDCHECK(hasHash_);
|
2018-06-01 19:29:47 +03:00
|
|
|
return hash_;
|
|
|
|
}
|
|
|
|
|
2018-10-24 04:48:38 +03:00
|
|
|
std::optional<Hash> getOptionalHash() const {
|
2018-06-01 19:29:47 +03:00
|
|
|
if (hasHash_) {
|
|
|
|
return hash_;
|
|
|
|
} else {
|
2018-10-24 04:48:38 +03:00
|
|
|
return std::nullopt;
|
2018-06-01 19:29:47 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
InodeNumber getInodeNumber() const;
|
|
|
|
|
|
|
|
void setMaterialized() {
|
|
|
|
hasHash_ = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void setDematerialized(Hash hash) {
|
2020-11-11 03:29:24 +03:00
|
|
|
XDCHECK(hasInodePointer_);
|
2018-06-01 19:29:47 +03:00
|
|
|
hasHash_ = true;
|
|
|
|
hash_ = hash;
|
|
|
|
}
|
|
|
|
|
2018-06-01 21:57:49 +03:00
|
|
|
/**
|
|
|
|
* Returns the mode specified when this inode was created (whether from source
|
|
|
|
* control or via mkdir/mknod/creat).
|
|
|
|
*
|
|
|
|
* Note: when the mode_t for an inode changes, this value does not update.
|
|
|
|
*/
|
|
|
|
mode_t getInitialMode() const {
|
2020-03-20 20:53:03 +03:00
|
|
|
return static_cast<mode_t>(initialMode_);
|
2018-06-01 19:29:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the file type, as a dtype_t value as used by readdir()
|
|
|
|
*
|
|
|
|
* It is okay for callers to call getDtype() even if the inode is
|
|
|
|
* loaded. The file type for an existing entry never changes.
|
|
|
|
*/
|
|
|
|
dtype_t getDtype() const {
|
2020-03-20 20:53:03 +03:00
|
|
|
return mode_to_dtype(getInitialMode());
|
2018-06-01 19:29:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if the entry is a directory or not.
|
|
|
|
*
|
|
|
|
* It is okay for callers to call isDirectory() even if the inode is
|
|
|
|
* loaded. The file type for an existing entry never changes.
|
|
|
|
*/
|
|
|
|
bool isDirectory() const {
|
|
|
|
return dtype_t::Dir == getDtype();
|
|
|
|
}
|
|
|
|
|
|
|
|
InodeBase* getInode() const {
|
|
|
|
return hasInodePointer_ ? inode_ : nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
InodePtr getInodePtr() const {
|
|
|
|
// It's safe to call newPtrLocked because calling getInode() implies the
|
|
|
|
// TreeInode's contents_ lock is held.
|
|
|
|
return hasInodePointer_ ? InodePtr::newPtrLocked(inode_) : InodePtr{};
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Same as getInodePtr().asFilePtrOrNull() except it avoids constructing
|
|
|
|
* a FileInodePtr if the entry does not point to a FileInode.
|
|
|
|
*/
|
|
|
|
FileInodePtr asFilePtrOrNull() const;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Same as getInodePtr().asTreePtrOrNull() except it avoids constructing
|
|
|
|
* a TreeInodePtr if the entry does not point to a FileInode.
|
|
|
|
*/
|
|
|
|
TreeInodePtr asTreePtrOrNull() const;
|
|
|
|
|
2018-07-18 07:33:22 +03:00
|
|
|
/**
|
|
|
|
* Associates a loaded inode pointer with this entry. Does not take ownership.
|
|
|
|
*/
|
2018-06-01 19:29:47 +03:00
|
|
|
void setInode(InodeBase* inode);
|
|
|
|
|
2018-07-18 07:33:22 +03:00
|
|
|
/**
|
|
|
|
* Clears and returns this entry's inode pointer. Must only be called if
|
|
|
|
* assignInode() has.
|
|
|
|
*
|
|
|
|
* This method is only called when the inode is being unloaded and its pointer
|
|
|
|
* is no longer valid.
|
|
|
|
*/
|
|
|
|
FOLLY_NODISCARD InodeBase* clearInode();
|
2018-06-01 19:29:47 +03:00
|
|
|
|
|
|
|
private:
|
|
|
|
/**
|
2018-06-01 21:57:49 +03:00
|
|
|
* The initial entry type for this entry. Two bits are borrowed from the top
|
|
|
|
* so the entire struct fits in four words.
|
|
|
|
*
|
|
|
|
* TODO: This field is not updated when an inode's mode bits are changed.
|
|
|
|
* For now, it's used primarily to migrate data without loss from the old
|
|
|
|
* Overlay Dir storage. After the InodeMetadataTable is in use for a while,
|
|
|
|
* this should be replaced with dtype_t and the bitfields can go away.
|
|
|
|
*/
|
2020-03-20 20:53:03 +03:00
|
|
|
uint32_t initialMode_ : 30;
|
2018-06-01 21:57:49 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Whether the hash_ field matches the contents from source control. If
|
|
|
|
* hasHash_ is false, the entry is materialized.
|
2018-06-01 19:29:47 +03:00
|
|
|
*/
|
2020-03-20 20:53:03 +03:00
|
|
|
uint32_t hasHash_ : 1;
|
2018-06-01 19:29:47 +03:00
|
|
|
|
2018-06-01 21:57:49 +03:00
|
|
|
/**
|
|
|
|
* If true, the inode_ field is valid. If false, inodeNumber_ is valid.
|
2020-11-03 21:55:20 +03:00
|
|
|
* Synonymous with the inode being "loaded".
|
2018-06-01 21:57:49 +03:00
|
|
|
*/
|
2020-03-20 20:53:03 +03:00
|
|
|
uint32_t hasInodePointer_ : 1;
|
2018-06-01 19:29:47 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* If the entry is not materialized, this contains the hash
|
|
|
|
* identifying the source control Tree (if this is a directory) or Blob
|
|
|
|
* (if this is a file) that contains the entry contents.
|
|
|
|
*
|
|
|
|
* If the entry is materialized, hasHash_ is false.
|
|
|
|
*
|
|
|
|
* TODO: If inode is set, this field generally should not be used, and the
|
|
|
|
* child InodeBase should be consulted instead.
|
|
|
|
*/
|
|
|
|
Hash hash_;
|
|
|
|
|
|
|
|
union {
|
|
|
|
/**
|
|
|
|
* The inode number, if one is allocated for this entry, or 0 if one is
|
|
|
|
* not allocated.
|
|
|
|
*
|
|
|
|
* An inode number is required for materialized entries, so this is always
|
|
|
|
* non-zero if hash_ is not set. (It may also be non-zero even when hash_
|
|
|
|
* is set.)
|
|
|
|
*/
|
|
|
|
InodeNumber inodeNumber_{0};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A pointer to the child inode, if it is loaded, or null if it is not
|
|
|
|
* loaded.
|
|
|
|
*
|
|
|
|
* Note that we store this as a raw pointer. Children inodes hold a
|
|
|
|
* reference to their parent TreeInode, not the other way around.
|
|
|
|
* Children inodes can be destroyed only in one of two ways:
|
|
|
|
* - Being unlinked, then having their last reference go away.
|
|
|
|
* In this case they will be removed from our entries list when they are
|
|
|
|
* unlinked.
|
|
|
|
* - Being unloaded (after their reference count is already 0). In this
|
|
|
|
* case the parent TreeInodes responsible for triggering unloading of
|
|
|
|
* its children, so it resets this pointer to null when it unloads the
|
|
|
|
* child.
|
|
|
|
*/
|
|
|
|
InodeBase* inode_;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2018-06-01 21:57:49 +03:00
|
|
|
static_assert(sizeof(DirEntry) == 32, "DirEntry is four words");
|
2018-06-01 19:29:47 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Represents a directory in the overlay.
|
|
|
|
*/
|
2020-09-18 18:39:07 +03:00
|
|
|
struct DirContents : PathMap<DirEntry> {
|
2021-04-20 23:07:31 +03:00
|
|
|
explicit DirContents(CaseSensitivity caseSensitive)
|
|
|
|
: PathMap(caseSensitive) {}
|
2020-09-18 18:39:07 +03:00
|
|
|
};
|
2018-06-01 19:29:47 +03:00
|
|
|
|
|
|
|
} // namespace eden
|
|
|
|
} // namespace facebook
|