📄 dir.c
字号:
/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*- * vim:expandtab:shiftwidth=8:tabstop=8: * * Directory code for lustre client. * * Copyright (C) 2002--2007 Cluster File Systems, Inc. * * This file is part of the Lustre file system, http://www.lustre.org * Lustre is a trademark of Cluster File Systems, Inc. * * You may have signed or agreed to another license before downloading * this software. If so, you are bound by the terms and conditions * of that agreement, and the following does not apply to you. See the * LICENSE file included with this distribution for more information. * * If you did not agree to a different license, then this copy of Lustre * is open source software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * In either case, Lustre is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * license text for more details. * */#include <linux/fs.h>#include <linux/pagemap.h>#include <linux/mm.h>#include <linux/version.h>#include <linux/smp_lock.h>#include <asm/uaccess.h>#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))# include <linux/locks.h> // for wait_on_buffer#else# include <linux/buffer_head.h> // for wait_on_buffer#endif#define DEBUG_SUBSYSTEM S_LLITE#include <obd_support.h>#include <obd_class.h>#include <lustre_lib.h>#include <lustre/lustre_idl.h>#include <lustre_lite.h>#include <lustre_dlm.h>#include "llite_internal.h"/* * Directory entries are currently in the same format as ext2/ext3, but will * be changed in the future to accomodate FIDs */#define LL_DIR_NAME_LEN (255)static const int LL_DIR_PAD = 4;struct ll_dir_entry { /* number of inode, referenced by this entry */ __le32 lde_inode; /* total record length, multiple of LL_DIR_PAD */ __le16 lde_rec_len; /* length of name */ __u8 lde_name_len; /* file type: regular, directory, device, etc. */ __u8 lde_file_type; /* name. NOT NUL-terminated */ char lde_name[LL_DIR_NAME_LEN];};static inline unsigned ll_dir_rec_len(unsigned name_len){ return (name_len + 8 + LL_DIR_PAD - 1) & ~(LL_DIR_PAD - 1);}#ifndef HAVE_PAGE_CHECKED#ifdef HAVE_PG_FS_MISC#define PageChecked(page) test_bit(PG_fs_misc, &(page)->flags)#define SetPageChecked(page) set_bit(PG_fs_misc, &(page)->flags)#else#error PageChecked or PageFsMisc not defined in kernel#endif#endif/* returns the page unlocked, but with a reference */static int ll_dir_readpage(struct file *file, struct page *page){ struct inode *inode = page->mapping->host; struct ll_fid mdc_fid; __u64 offset; struct ptlrpc_request *request; struct mds_body *body; int rc = 0; ENTRY; offset = (__u64)page->index << CFS_PAGE_SHIFT; CDEBUG(D_VFSTRACE, "VFS Op:inode=%lu/%u(%p) off "LPU64"\n", inode->i_ino, inode->i_generation, inode, offset); mdc_pack_fid(&mdc_fid, inode->i_ino, inode->i_generation, S_IFDIR); rc = mdc_readpage(ll_i2sbi(inode)->ll_mdc_exp, &mdc_fid, offset, page, &request); if (!rc) { body = lustre_msg_buf(request->rq_repmsg, REPLY_REC_OFF, sizeof(*body)); LASSERT(body != NULL); /* checked by mdc_readpage() */ /* swabbed by mdc_readpage() */ LASSERT(lustre_rep_swabbed(request, REPLY_REC_OFF)); if (body->size != i_size_read(inode)) { ll_inode_size_lock(inode, 0); i_size_write(inode, body->size); ll_inode_size_unlock(inode, 0); } SetPageUptodate(page); } ptlrpc_req_finished(request); unlock_page(page); EXIT; return rc;}struct address_space_operations ll_dir_aops = { .readpage = ll_dir_readpage,};static inline unsigned ll_dir_page_mask(struct inode *inode){ return ~(inode->i_sb->s_blocksize - 1);}/* * Check consistency of a single entry. */static int ll_dir_check_entry(struct inode *dir, struct ll_dir_entry *ent, unsigned offset, unsigned rec_len, pgoff_t index){ const char *msg; /* * Consider adding more checks. */ if (unlikely(rec_len < ll_dir_rec_len(1))) msg = "entry is too short"; else if (unlikely(rec_len & 3)) msg = "wrong alignment"; else if (unlikely(rec_len < ll_dir_rec_len(ent->lde_name_len))) msg = "rec_len doesn't match name_len"; else if (unlikely(((offset + rec_len - 1) ^ offset) & ll_dir_page_mask(dir))) msg = "directory entry across blocks"; else return 0; CERROR("%s: bad entry in directory %lu/%u: %s - " "offset=%lu+%u, inode=%lu, rec_len=%d," " name_len=%d\n", ll_i2mdcexp(dir)->exp_obd->obd_name, dir->i_ino, dir->i_generation, msg, index << CFS_PAGE_SHIFT, offset, (unsigned long)le32_to_cpu(ent->lde_inode), rec_len, ent->lde_name_len); return -EIO;}static inline struct ll_dir_entry *ll_entry_at(void *base, unsigned offset){ return (struct ll_dir_entry *)(base + offset);}static void ll_dir_check_page(struct inode *dir, struct page *page){ int err; unsigned size = dir->i_sb->s_blocksize; char *addr = page_address(page); unsigned off; unsigned limit; unsigned reclen; struct ll_dir_entry *ent; err = 0; if ((i_size_read(dir) >> CFS_PAGE_SHIFT) == (__u64)page->index) { /* * Last page. */ limit = i_size_read(dir) & ~CFS_PAGE_MASK; if (limit & (size - 1)) { CERROR("%s: dir %lu/%u size %llu doesn't match %u\n", ll_i2mdcexp(dir)->exp_obd->obd_name, dir->i_ino, dir->i_generation, i_size_read(dir), size); err++; } else { /* * Place dummy forwarding entries to streamline * ll_readdir(). */ for (off = limit; off < CFS_PAGE_SIZE; off += size) { ent = ll_entry_at(addr, off); ent->lde_rec_len = cpu_to_le16(size); ent->lde_name_len = 0; ent->lde_inode = 0; } } } else limit = CFS_PAGE_SIZE; for (off = 0; !err && off <= limit - ll_dir_rec_len(1); off += reclen) { ent = ll_entry_at(addr, off); reclen = le16_to_cpu(ent->lde_rec_len); err = ll_dir_check_entry(dir, ent, off, reclen, page->index); } if (!err && off != limit) { ent = ll_entry_at(addr, off); CERROR("%s: entry in directory %lu/%u spans the page boundary " "offset="LPU64"+%u, inode=%lu\n", ll_i2mdcexp(dir)->exp_obd->obd_name, dir->i_ino, dir->i_generation, (__u64)page->index << CFS_PAGE_SHIFT, off, (unsigned long)le32_to_cpu(ent->lde_inode)); err++; } if (err) SetPageError(page); SetPageChecked(page);}struct page *ll_get_dir_page(struct inode *dir, unsigned long n){ struct ldlm_res_id res_id = { .name = { dir->i_ino, (__u64)dir->i_generation} }; struct lustre_handle lockh; struct obd_device *obddev = class_exp2obd(ll_i2sbi(dir)->ll_mdc_exp); struct address_space *mapping = dir->i_mapping; struct page *page; ldlm_policy_data_t policy = {.l_inodebits = {MDS_INODELOCK_UPDATE} }; int rc; rc = ldlm_lock_match(obddev->obd_namespace, LDLM_FL_BLOCK_GRANTED, &res_id, LDLM_IBITS, &policy, LCK_CR, &lockh); if (!rc) { struct lookup_intent it = { .it_op = IT_READDIR }; struct ldlm_enqueue_info einfo = { LDLM_IBITS, LCK_CR, ll_mdc_blocking_ast, ldlm_completion_ast, NULL, dir }; struct ptlrpc_request *request; struct mdc_op_data data; ll_prepare_mdc_op_data(&data, dir, NULL, NULL, 0, 0, NULL); rc = mdc_enqueue(ll_i2sbi(dir)->ll_mdc_exp, &einfo, &it, &data, &lockh, NULL, 0, 0); request = (struct ptlrpc_request *)it.d.lustre.it_data; if (request) ptlrpc_req_finished(request); if (rc < 0) { CERROR("lock enqueue: rc: %d\n", rc); return ERR_PTR(rc); } } ldlm_lock_dump_handle(D_OTHER, &lockh); page = read_cache_page(mapping, n, (filler_t*)mapping->a_ops->readpage, NULL); if (IS_ERR(page)) GOTO(out_unlock, page); wait_on_page(page); (void)kmap(page); if (!PageUptodate(page)) goto fail; if (!PageChecked(page)) ll_dir_check_page(dir, page); if (PageError(page)) goto fail;out_unlock: ldlm_lock_decref(&lockh, LCK_CR); return page;fail: kunmap(page); page_cache_release(page); page = ERR_PTR(-EIO); goto out_unlock;}/* * p is at least 6 bytes before the end of page */static inline struct ll_dir_entry *ll_dir_next_entry(struct ll_dir_entry *p){ return ll_entry_at(p, le16_to_cpu(p->lde_rec_len));}static inline unsigned ll_dir_validate_entry(char *base, unsigned offset, unsigned mask){ struct ll_dir_entry *de = ll_entry_at(base, offset); struct ll_dir_entry *p = ll_entry_at(base, offset & mask); while (p < de && p->lde_rec_len > 0) p = ll_dir_next_entry(p); return (char *)p - base;}/* * File type constants. The same as in ext2 for compatibility. */enum { LL_DIR_FT_UNKNOWN, LL_DIR_FT_REG_FILE, LL_DIR_FT_DIR, LL_DIR_FT_CHRDEV, LL_DIR_FT_BLKDEV, LL_DIR_FT_FIFO, LL_DIR_FT_SOCK, LL_DIR_FT_SYMLINK, LL_DIR_FT_MAX};static unsigned char ll_dir_filetype_table[LL_DIR_FT_MAX] = { [LL_DIR_FT_UNKNOWN] = DT_UNKNOWN, [LL_DIR_FT_REG_FILE] = DT_REG, [LL_DIR_FT_DIR] = DT_DIR, [LL_DIR_FT_CHRDEV] = DT_CHR, [LL_DIR_FT_BLKDEV] = DT_BLK, [LL_DIR_FT_FIFO] = DT_FIFO, [LL_DIR_FT_SOCK] = DT_SOCK, [LL_DIR_FT_SYMLINK] = DT_LNK,};/* * Process one page. Returns: * * -ve: filldir commands readdir to stop. * +ve: number of entries submitted to filldir. * 0: no live entries on this page. */int ll_readdir_page(char *addr, __u64 base, unsigned *offset, filldir_t filldir, void *cookie){ struct ll_dir_entry *de; char *end; int nr; de = ll_entry_at(addr, *offset); end = addr + CFS_PAGE_SIZE - ll_dir_rec_len(1); for (nr = 0 ;(char*)de <= end; de = ll_dir_next_entry(de)) { if (de->lde_inode != 0) { nr++; *offset = (char *)de - addr; if (filldir(cookie, de->lde_name, de->lde_name_len, base | *offset, le32_to_cpu(de->lde_inode), ll_dir_filetype_table[de->lde_file_type & (LL_DIR_FT_MAX - 1)])) return -1; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -