📄 kfsops.cc
字号:
return -ENOENT; extractAll(l, dkey, v); assert(v.size() >= 2); return 0;}/*! * \brief return a file's chunk information (if any) * \param[in] file file id for the file * \param[out] v vector of MetaChunkInfo results * \return status code */intTree::getalloc(fid_t file, vector <MetaChunkInfo *> &v){ const Key ckey(KFS_CHUNKINFO, file, Key::MATCH_ANY); Node *l = findLeaf(ckey); if (l != NULL) extractAll(l, ckey, v); return 0;}/*! * \brief return the specific chunk information from a file * \param[in] file file id for the file * \param[in] offset offset in the file * \param[out] c MetaChunkInfo * \return status code */intTree::getalloc(fid_t file, chunkOff_t offset, MetaChunkInfo **c){ // Allocation information is stored for offset's in the file that // correspond to chunk boundaries. chunkOff_t boundary = chunkStartOffset(offset); const Key ckey(KFS_CHUNKINFO, file, boundary); Node *l = findLeaf(ckey); if (l == NULL) return -ENOENT; *c = l->extractMeta<MetaChunkInfo>(ckey); return 0;}class ChunkIdMatch { chunkId_t myid;public: ChunkIdMatch(seq_t c) : myid(c) { } bool operator() (MetaChunkInfo *m) { return m->chunkId == myid; }};/*! * \brief Retrieve the chunk-version for a file/chunkId * \param[in] file file id for the file * \param[in] chunkId chunkId of interest * \param[out] chunkVersion the version # of chunkId if such * a chunkId exists; 0 otherwise * \return status code*/intTree::getChunkVersion(fid_t file, chunkId_t chunkId, seq_t *chunkVersion){ vector <MetaChunkInfo *> v; vector <MetaChunkInfo *>::iterator i; MetaChunkInfo *m; *chunkVersion = 0; getalloc(file, v); i = find_if(v.begin(), v.end(), ChunkIdMatch(chunkId)); if (i == v.end()) return -ENOENT; m = *i; *chunkVersion = m->chunkVersion; return 0;}/*! * \brief allocate a chunk id for a file. * \param[in] file file id for the file * \param[in] offset offset in the file * \param[out] chunkId chunkId that is (pre) allocated. Allocation * is a two-step process: we grab a chunkId and then try to place the * chunk on a chunkserver; only when placement succeeds can the * chunkId be assigned to the file. This function does the part of * grabbing the chunkId. * \param[out] chunkVersion The version # assigned to the chunk * \return status code */intTree::allocateChunkId(fid_t file, chunkOff_t offset, chunkId_t *chunkId, seq_t *chunkVersion, int16_t *numReplicas){ MetaFattr *fa = getFattr(file); if (fa == NULL) return -ENOENT; if (numReplicas != NULL) { assert(fa->numReplicas != 0); *numReplicas = fa->numReplicas; } // Allocation information is stored for offset's in the file that // correspond to chunk boundaries. This simplifies finding // allocation information as we need to look for chunk // starting locations only. assert(offset % CHUNKSIZE == 0); chunkOff_t boundary = chunkStartOffset(offset); const Key ckey(KFS_CHUNKINFO, file, boundary); Node *l = findLeaf(ckey); // check if an id has already been assigned to this offset if (l != NULL) { MetaChunkInfo *c = l->extractMeta<MetaChunkInfo>(ckey); *chunkId = c->chunkId; *chunkVersion = c->chunkVersion; return -EEXIST; } // during replay chunkId will be non-zero. In such cases, // don't do new allocation. if (*chunkId == 0) { *chunkId = chunkID.genid(); *chunkVersion = chunkVersionInc; } return 0;}/*! * \brief update the metatree to link an allocated a chunk id with * its associated file. * \param[in] file file id for the file * \param[in] offset offset in the file * \param[in] chunkId chunkId that is (pre) allocated. Allocation * is a two-step process: we grab a chunkId and then try to place the * chunk on a chunkserver; only when placement succeeds can the * chunkId be assigned to the file. This function does the part of * assinging the chunkId to the file. * \param[in] chunkVersion chunkVersion that is (pre) assigned. * \return status code */intTree::assignChunkId(fid_t file, chunkOff_t offset, chunkId_t chunkId, seq_t chunkVersion){ chunkOff_t boundary = chunkStartOffset(offset); MetaFattr *fa = getFattr(file); if (fa == NULL) return -ENOENT; // check if an id has already been assigned to this chunk const Key ckey(KFS_CHUNKINFO, file, boundary); Node *l = findLeaf(ckey); if (l != NULL) { MetaChunkInfo *c = l->extractMeta<MetaChunkInfo>(ckey); chunkId = c->chunkId; if (c->chunkVersion == chunkVersion) return -EEXIST; c->chunkVersion = chunkVersion; return 0; } MetaChunkInfo *m = new MetaChunkInfo(file, offset, chunkId, chunkVersion); if (insert(m)) { // insert failed delete m; panic("assignChunk", false); } // insert succeeded; so, bump the chunkcount. fa->chunkcount++; // we will know the size of the file only when the write to this chunk // is finished. so, until then.... fa->filesize = -1; gettimeofday(&fa->mtime, NULL); return 0;}static boolChunkInfo_compare(MetaChunkInfo *first, MetaChunkInfo *second){ return first->offset < second->offset;}intTree::truncate(fid_t file, chunkOff_t offset, chunkOff_t *allocOffset){ MetaFattr *fa = getFattr(file); if (fa == NULL) return -ENOENT; if (fa->type != KFS_FILE) return -EISDIR; vector <MetaChunkInfo *> chunkInfo; vector <MetaChunkInfo *>::iterator m; getalloc(fa->id(), chunkInfo); assert(fa->chunkcount == (long long)chunkInfo.size()); fa->filesize = -1; // compute the starting offset for what will be the // "last" chunk for the file chunkOff_t lastChunkStartOffset = chunkStartOffset(offset); MetaChunkInfo last(fa->id(), lastChunkStartOffset, 0, 0); m = lower_bound(chunkInfo.begin(), chunkInfo.end(), &last, ChunkInfo_compare); // // If there is no chunk corresponding to the offset to which // the file should be truncated, allocate one at that point. // This can happen due to the following cases: // 1. The offset to truncate to exceeds the last chunk of // the file. // 2. There is a hole in the file and the offset to truncate // to corresponds to the hole. // if ((m == chunkInfo.end()) || ((*m)->offset != lastChunkStartOffset)) { // Allocate a chunk at this offset *allocOffset = lastChunkStartOffset; return 1; } if ((*m)->offset <= offset) { // truncate the last chunk so that the file // has the desired size. (*m)->TruncateChunk(offset - (*m)->offset); ++m; } // delete everything past the last chunk while (m != chunkInfo.end()) { (*m)->DeleteChunk(); ++m; fa->chunkcount--; } gettimeofday(&fa->mtime, NULL); return 0;}/*! * \brief check whether one directory is a descendant of another * \param[in] src file ID of possible ancestor * \param[in] dst file ID of possible descendant * * Check dst and each of its ancestors to see whether src is * among them; used to avoid making a directory into its own * child via rename. */boolTree::is_descendant(fid_t src, fid_t dst){ while (src != dst && dst != ROOTFID) { MetaFattr *dotdot = lookup(dst, ".."); dst = dotdot->id(); } return (src == dst);}/*! * \brief rename a file or directory * \param[in] parent file id of parent directory * \param[in] oldname the file's current name * \param[in] newname the new name for the file * \param[in] overwrite when set, overwrite the dest if it exists * \return status code */intTree::rename(fid_t parent, const string &oldname, string &newname, bool overwrite){ int status; MetaDentry *src = getDentry(parent, oldname); if (src == NULL) return -ENOENT; fid_t ddir; string dname; string::size_type rslash = newname.rfind('/'); if (rslash == string::npos) { ddir = parent; dname = newname; } else { MetaFattr *ddfattr = lookupPath( parent, newname.substr(0, rslash)); if (ddfattr == NULL) return -ENOENT; else if (ddfattr->type != KFS_DIR) return -ENOTDIR; else ddir = ddfattr->id(); dname = newname.substr(rslash + 1); } if (!legalname(dname)) return -EINVAL; if (ddir == parent && dname == oldname) return 0; MetaFattr *sfattr = lookup(parent, oldname); MetaFattr *dfattr = lookup(ddir, dname); bool dexists = (dfattr != NULL); FileType t = sfattr->type; if ((!overwrite) && dexists) return -EEXIST; if (dexists && t != dfattr->type) return (t == KFS_DIR) ? -ENOTDIR : -EISDIR; if (dexists && t == KFS_DIR && !emptydir(dfattr->id())) return -ENOTEMPTY; if (t == KFS_DIR && is_descendant(sfattr->id(), ddir)) return -EINVAL; if (dexists) { status = (t == KFS_DIR) ? rmdir(ddir, dname) : remove(ddir, dname); if (status != 0) return status; } fid_t srcFid = src->id(); if (t == KFS_DIR) { // get rid of the linkage of the "old" .. unlink(srcFid, "..", sfattr, true); } status = del(src); assert(status == 0); MetaDentry *newSrc = new MetaDentry(ddir, dname, srcFid); status = insert(newSrc); assert(status == 0); if (t == KFS_DIR) { // create a new linkage for .. status = link(srcFid, "..", KFS_DIR, ddir, 1); assert(status == 0); } return 0;}/*! * \brief Change the degree of replication for a file. * \param[in] dir file id of the file * \param[in] numReplicas desired degree of replication * \return status code (-errno on failure) */intTree::changeFileReplication(fid_t fid, int16_t numReplicas){ MetaFattr *fa = getFattr(fid); vector<MetaChunkInfo*> chunkInfo; if (fa == NULL) return -ENOENT; fa->setReplication(numReplicas); getalloc(fid, chunkInfo); for (vector<ChunkLayoutInfo>::size_type i = 0; i < chunkInfo.size(); ++i) { gLayoutManager.ChangeChunkReplication(chunkInfo[i]->chunkId); } return 0;}/*! * \brief A file that has to be removed is currently busy. So, rename the * file to the dumpster and we'll clean it up later. * \param[in] dir file id of the parent directory * \param[in] fname file name * \return status code (zero on success) */intTree::moveToDumpster(fid_t dir, const string &fname){ static uint64_t counter = 1; string tempname = "/" + DUMPSTERDIR + "/"; MetaFattr *fa = lookup(ROOTFID, DUMPSTERDIR); if (fa == NULL) { // Someone nuked the dumpster makeDumpsterDir(); fa = lookup(ROOTFID, DUMPSTERDIR); if (fa == NULL) { assert(!"No dumpster"); KFS_LOG_VA_INFO("Unable to create dumpster dir to remove %s", fname.c_str()); return -1; } } // can't move something in the dumpster back to dumpster if (fa->id() == dir) return -EEXIST; // generate a unique name tempname += fname + boost::lexical_cast<string>(counter); counter++; return rename(dir, fname, tempname, true);}class RemoveDumpsterEntry { fid_t dir;public: RemoveDumpsterEntry(fid_t d) : dir(d) { } void operator() (MetaDentry *e) { metatree.remove(dir, e->getName()); }};/*! * \brief Periodically, cleanup the dumpster and reclaim space. If * the lease issued on a file has expired, then the file can be nuked. */voidTree::cleanupDumpster(){ MetaFattr *fa = lookup(ROOTFID, DUMPSTERDIR); if (fa == NULL) { // Someone nuked the dumpster makeDumpsterDir(); } fid_t dir = fa->id(); vector <MetaDentry *> v; readdir(dir, v); for_each(v.begin(), v.end(), RemoveDumpsterEntry(dir));}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -