📄 kfsops.cc
字号:
/*! * $Id: kfsops.cc 232 2009-01-01 20:40:09Z lohitvijayarenu $ * * \file kfsops.cc * \brief KFS file system operations. * \author Blake Lewis and Sriram Rao * * Copyright 2008 Quantcast Corp. * Copyright 2006-2008 Kosmix Corp. * * This file is part of Kosmos File System (KFS). * * Licensed under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. See the License for the specific language governing * permissions and limitations under the License. */#include "kfstypes.h"#include "kfstree.h"#include "util.h"#include "LayoutManager.h"#include <algorithm>#include <functional>#include <boost/lexical_cast.hpp>#include "common/log.h"#include "common/config.h"using std::mem_fun;using std::for_each;using std::find_if;using std::lower_bound;using std::cout;using std::endl;using namespace KFS;const string DUMPSTERDIR("dumpster");/*! * \brief Make a dumpster directory into which we can rename busy * files. When the file is non-busy, it is nuked from the dumpster. */voidKFS::makeDumpsterDir(){ fid_t dummy = 0; metatree.mkdir(ROOTFID, DUMPSTERDIR, &dummy);}/*! * \brief Cleanup the dumpster directory on startup. Also, if * the dumpster doesn't exist, make one. */voidKFS::emptyDumpsterDir(){ makeDumpsterDir(); metatree.cleanupDumpster();}/*! * \brief check file name for legality * * Legal means nonempty and not containing any slashes. * * \param[in] name to check * \return true if name is legal */static boollegalname(const string name){ return (!name.empty() && name.find('/', 0) == string::npos);}/*! * \brief see whether path is absolute */static boolabsolute(const string path){ return (path[0] == '/');}/*! * \brief common code for create and mkdir * \param[in] dir fid of parent directory * \param[in] fname name of object to be created * \param[in] type file or directory * \param[in] myID fid of new object * \param[in] numReplicas desired degree of replication for file * * Create a directory entry and file attributes for the new object. * But don't create attributes for "." and ".." links, since these * share the directory's attributes. */intTree::link(fid_t dir, const string fname, FileType type, fid_t myID, int16_t numReplicas){ assert(legalname(fname)); MetaDentry *dentry = new MetaDentry(dir, fname, myID); insert(dentry); if (fname != "." && fname != "..") { MetaFattr *fattr = new MetaFattr(type, dentry->id(), numReplicas); insert(fattr); } return 0;}/*! * \brief create a new file * \param[in] dir file id of the parent directory * \param[in] fname file name * \param[out] newFid id of new file * \param[in] numReplicas desired degree of replication for file * \param[in] exclusive model the O_EXCL flag of open() * * \return status code (zero on success) */intTree::create(fid_t dir, const string &fname, fid_t *newFid, int16_t numReplicas, bool exclusive){ if (!legalname(fname)) { KFS_LOG_VA_WARN("Bad file name %s", fname.c_str()); return -EINVAL; } if (numReplicas <= 0) { KFS_LOG_VA_DEBUG("Bad # of replicas (%d) for %s", numReplicas, fname.c_str()); return -EINVAL; } MetaFattr *fa = lookup(dir, fname); if (fa != NULL) { if (fa->type != KFS_FILE) return -EISDIR; // Model O_EXECL behavior in create: if the file exists // and exclusive is specified, fail the create. if (exclusive) return -EEXIST; int status = remove(dir, fname); if (status == -EBUSY) { KFS_LOG_VA_INFO("Remove failed as file (%d:%s) is busy", dir, fname.c_str()); return status; } assert(status == 0); } if (*newFid == 0) *newFid = fileID.genid(); return link(dir, fname, KFS_FILE, *newFid, numReplicas);}/*! * \brief common code for remove and rmdir * \param[in] dir fid of parent directory * \param[in] fname name of item to be removed * \param[in] fa attributes for removed item * \pamam[in] save_fa don't delete attributes if true * * save_fa prevents multiple deletions when removing * the "." and ".." links to a directory. */voidTree::unlink(fid_t dir, const string fname, MetaFattr *fa, bool save_fa){ MetaDentry dentry(dir, fname, fa->id()); int UNUSED_ATTR status = del(&dentry); assert(status == 0); if (!save_fa) { status = del(fa); assert(status == 0); }}/*! * \brief remove a file * \param[in] dir file id of the parent directory * \param[in] fname file name * \return status code (zero on success) */intTree::remove(fid_t dir, const string &fname){ MetaFattr *fa = lookup(dir, fname); if (fa == NULL) return -ENOENT; if (fa->type != KFS_FILE) return -EISDIR; if (fa->chunkcount > 0) { vector <MetaChunkInfo *> chunkInfo; getalloc(fa->id(), chunkInfo); assert(fa->chunkcount == (long long)chunkInfo.size()); if (gLayoutManager.IsValidLeaseIssued(chunkInfo)) { // put the file into dumpster int status = moveToDumpster(dir, fname); KFS_LOG_VA_DEBUG("Moving %s to dumpster", fname.c_str()); return status; } // fire-away... for_each(chunkInfo.begin(), chunkInfo.end(), mem_fun(&MetaChunkInfo::DeleteChunk)); } unlink(dir, fname, fa, false); return 0;}/*! * \brief create a new directory * \param[in] dir file id of the parent directory * \param[in] dname name of new directory * \param[out] newFid id of new directory * \return status code (zero on success) */intTree::mkdir(fid_t dir, const string &dname, fid_t *newFid){ if (!legalname(dname) && dir != ROOTFID && dname != "/") return -EINVAL; if (lookup(dir, dname) != NULL) return -EEXIST; fid_t myID = *newFid; if (myID == 0) myID = (dname == "/") ? dir : fileID.genid(); MetaDentry *dentry = new MetaDentry(dir, dname, myID); MetaFattr *fattr = new MetaFattr(KFS_DIR, dentry->id(), 1); insert(dentry); insert(fattr); int status = link(myID, ".", KFS_DIR, myID, 1); if (status != 0) panic("link(.)",false); status = link(myID, "..", KFS_DIR, dir, 1); if (status != 0) panic("link(..)", false); *newFid = myID; return 0;}/*! * \brief check whether a directory is empty * \param[in] dir file ID of the directory */boolTree::emptydir(fid_t dir){ vector <MetaDentry *> v; readdir(dir, v); return (v.size() == 2);}/*! * \brief remove a directory * \param[in] dir file id of the parent directory * \param[in] dname name of directory * \return status code (zero on success) */intTree::rmdir(fid_t dir, const string &dname){ MetaFattr *fa = lookup(dir, dname); if ((dir == ROOTFID) && (dname == DUMPSTERDIR)) { KFS_LOG_VA_INFO(" Preventing removing dumpster (%s)", dname.c_str()); return -EPERM; } if (fa == NULL) return -ENOENT; if (fa->type != KFS_DIR) return -ENOTDIR; if (dname == "." || dname == "..") return -EPERM; fid_t myID = fa->id(); if (!emptydir(myID)) return -ENOTEMPTY; unlink(myID, ".", fa, true); unlink(myID, "..", fa, true); unlink(dir, dname, fa, false); return 0;}/*! * \brief return attributes for the specified object * \param[in] fid the object's file id * \return pointer to the attributes */MetaFattr *Tree::getFattr(fid_t fid){ const Key fkey(KFS_FATTR, fid); Node *l = findLeaf(fkey); return (l == NULL) ? NULL : l->extractMeta<MetaFattr>(fkey);}MetaDentry *Tree::getDentry(fid_t dir, const string &fname){ vector <MetaDentry *> v; if (readdir(dir, v) != 0) return NULL; vector <MetaDentry *>::iterator d; d = find_if(v.begin(), v.end(), DirMatch(fname)); return (d == v.end()) ? NULL : *d;}/* * Map from file id to its directory entry. In the current instantation, this is SLOW: * we iterate over the leaves until we find the dentry. This method is needed * for KFS fsck, where we want to map from a fid -> name to reconstruct the * pathname for the file for which we want to print info (such as, missing * block, has fewer replicas etc. * \param[in] fid the object's file id * \return pointer to the attributes */MetaDentry *Tree::getDentry(fid_t fid){ LeafIter li(metatree.firstLeaf(), 0); Node *p = li.parent(); Meta *m = li.current(); MetaDentry *d = NULL; while (m != NULL) { if (m->id() == fid) { d = dynamic_cast<MetaDentry *>(m); if (d != NULL) break; } li.next(); p = li.parent(); m = (p == NULL) ? NULL : li.current(); } return d;}/* * Given a file-id, returns its fully qualified pathname. This involves * recursively traversing the metatree until the root directory. */stringTree::getPathname(fid_t fid){ MetaDentry *d; string s = ""; while (1) { d = getDentry(fid); if (d == NULL) return ""; if (s == "") s = d->getName(); else if (d->id() == ROOTFID) { return "/" + s; } else s = d->getName() + "/" + s; fid = d->getDir(); } return "";}/*! * \brief look up a file name and return its attributes * \param[in] dir file id of the parent directory * \param[in] fname file name that we are looking up * \return file attributes or NULL if not found */MetaFattr *Tree::lookup(fid_t dir, const string &fname){ MetaDentry *d = getDentry(dir, fname); if (d == NULL) return NULL; MetaFattr *fa = getFattr(d->id()); assert(fa != NULL); return fa;}/*! * \brief repeatedly apply Tree::lookup to an entire path * \param[in] rootdir file id of starting directory * \param[in] path the path to look up * \return attributes of the last component (or NULL) */MetaFattr *Tree::lookupPath(fid_t rootdir, const string &path){ string component; bool isabs = absolute(path); fid_t dir = (rootdir == 0 || isabs) ? ROOTFID : rootdir; string::size_type cstart = isabs ? 1 : 0; string::size_type slash = path.find('/', cstart); if (path.size() == cstart) return lookup(dir, "/"); while (slash != string::npos) { component.assign(path, cstart, slash - cstart); MetaFattr *fa = lookup(dir, component); if (fa == NULL) return NULL; dir = fa->id(); cstart = slash + 1; slash = path.find('/', cstart); } component.assign(path, cstart, path.size() - cstart); return lookup(dir, component);}/*! * \brief read the contents of a directory * \param[in] dir file id of directory * \param[out] v vector of directory entries * \return status code */intTree::readdir(fid_t dir, vector <MetaDentry *> &v){ const Key dkey(KFS_DENTRY, dir); Node *l = findLeaf(dkey); if (l == NULL)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -