/fs/afs/dir.c
C | 1184 lines | 864 code | 185 blank | 135 comment | 107 complexity | ad839e1eb73814c370737ef5526c6806 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.0, AGPL-1.0
- /* dir.c: AFS filesystem directory handling
- *
- * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
- * Written by David Howells (dhowells@redhat.com)
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- */
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/init.h>
- #include <linux/fs.h>
- #include <linux/namei.h>
- #include <linux/pagemap.h>
- #include <linux/ctype.h>
- #include <linux/sched.h>
- #include "internal.h"
- static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry,
- struct nameidata *nd);
- static int afs_dir_open(struct inode *inode, struct file *file);
- static int afs_readdir(struct file *file, void *dirent, filldir_t filldir);
- static int afs_d_revalidate(struct dentry *dentry, struct nameidata *nd);
- static int afs_d_delete(const struct dentry *dentry);
- static void afs_d_release(struct dentry *dentry);
- static int afs_lookup_filldir(void *_cookie, const char *name, int nlen,
- loff_t fpos, u64 ino, unsigned dtype);
- static int afs_create(struct inode *dir, struct dentry *dentry, int mode,
- struct nameidata *nd);
- static int afs_mkdir(struct inode *dir, struct dentry *dentry, int mode);
- static int afs_rmdir(struct inode *dir, struct dentry *dentry);
- static int afs_unlink(struct inode *dir, struct dentry *dentry);
- static int afs_link(struct dentry *from, struct inode *dir,
- struct dentry *dentry);
- static int afs_symlink(struct inode *dir, struct dentry *dentry,
- const char *content);
- static int afs_rename(struct inode *old_dir, struct dentry *old_dentry,
- struct inode *new_dir, struct dentry *new_dentry);
- const struct file_operations afs_dir_file_operations = {
- .open = afs_dir_open,
- .release = afs_release,
- .readdir = afs_readdir,
- .lock = afs_lock,
- .llseek = generic_file_llseek,
- };
- const struct inode_operations afs_dir_inode_operations = {
- .create = afs_create,
- .lookup = afs_lookup,
- .link = afs_link,
- .unlink = afs_unlink,
- .symlink = afs_symlink,
- .mkdir = afs_mkdir,
- .rmdir = afs_rmdir,
- .rename = afs_rename,
- .permission = afs_permission,
- .getattr = afs_getattr,
- .setattr = afs_setattr,
- };
- const struct dentry_operations afs_fs_dentry_operations = {
- .d_revalidate = afs_d_revalidate,
- .d_delete = afs_d_delete,
- .d_release = afs_d_release,
- .d_automount = afs_d_automount,
- };
- #define AFS_DIR_HASHTBL_SIZE 128
- #define AFS_DIR_DIRENT_SIZE 32
- #define AFS_DIRENT_PER_BLOCK 64
- union afs_dirent {
- struct {
- uint8_t valid;
- uint8_t unused[1];
- __be16 hash_next;
- __be32 vnode;
- __be32 unique;
- uint8_t name[16];
- uint8_t overflow[4]; /* if any char of the name (inc
- * NUL) reaches here, consume
- * the next dirent too */
- } u;
- uint8_t extended_name[32];
- };
- /* AFS directory page header (one at the beginning of every 2048-byte chunk) */
- struct afs_dir_pagehdr {
- __be16 npages;
- __be16 magic;
- #define AFS_DIR_MAGIC htons(1234)
- uint8_t nentries;
- uint8_t bitmap[8];
- uint8_t pad[19];
- };
- /* directory block layout */
- union afs_dir_block {
- struct afs_dir_pagehdr pagehdr;
- struct {
- struct afs_dir_pagehdr pagehdr;
- uint8_t alloc_ctrs[128];
- /* dir hash table */
- uint16_t hashtable[AFS_DIR_HASHTBL_SIZE];
- } hdr;
- union afs_dirent dirents[AFS_DIRENT_PER_BLOCK];
- };
- /* layout on a linux VM page */
- struct afs_dir_page {
- union afs_dir_block blocks[PAGE_SIZE / sizeof(union afs_dir_block)];
- };
- struct afs_lookup_cookie {
- struct afs_fid fid;
- const char *name;
- size_t nlen;
- int found;
- };
- /*
- * check that a directory page is valid
- */
- static inline void afs_dir_check_page(struct inode *dir, struct page *page)
- {
- struct afs_dir_page *dbuf;
- loff_t latter;
- int tmp, qty;
- #if 0
- /* check the page count */
- qty = desc.size / sizeof(dbuf->blocks[0]);
- if (qty == 0)
- goto error;
- if (page->index == 0 && qty != ntohs(dbuf->blocks[0].pagehdr.npages)) {
- printk("kAFS: %s(%lu): wrong number of dir blocks %d!=%hu\n",
- __func__, dir->i_ino, qty,
- ntohs(dbuf->blocks[0].pagehdr.npages));
- goto error;
- }
- #endif
- /* determine how many magic numbers there should be in this page */
- latter = dir->i_size - page_offset(page);
- if (latter >= PAGE_SIZE)
- qty = PAGE_SIZE;
- else
- qty = latter;
- qty /= sizeof(union afs_dir_block);
- /* check them */
- dbuf = page_address(page);
- for (tmp = 0; tmp < qty; tmp++) {
- if (dbuf->blocks[tmp].pagehdr.magic != AFS_DIR_MAGIC) {
- printk("kAFS: %s(%lu): bad magic %d/%d is %04hx\n",
- __func__, dir->i_ino, tmp, qty,
- ntohs(dbuf->blocks[tmp].pagehdr.magic));
- goto error;
- }
- }
- SetPageChecked(page);
- return;
- error:
- SetPageChecked(page);
- SetPageError(page);
- }
- /*
- * discard a page cached in the pagecache
- */
- static inline void afs_dir_put_page(struct page *page)
- {
- kunmap(page);
- page_cache_release(page);
- }
- /*
- * get a page into the pagecache
- */
- static struct page *afs_dir_get_page(struct inode *dir, unsigned long index,
- struct key *key)
- {
- struct page *page;
- _enter("{%lu},%lu", dir->i_ino, index);
- page = read_cache_page(dir->i_mapping, index, afs_page_filler, key);
- if (!IS_ERR(page)) {
- kmap(page);
- if (!PageChecked(page))
- afs_dir_check_page(dir, page);
- if (PageError(page))
- goto fail;
- }
- return page;
- fail:
- afs_dir_put_page(page);
- _leave(" = -EIO");
- return ERR_PTR(-EIO);
- }
- /*
- * open an AFS directory file
- */
- static int afs_dir_open(struct inode *inode, struct file *file)
- {
- _enter("{%lu}", inode->i_ino);
- BUILD_BUG_ON(sizeof(union afs_dir_block) != 2048);
- BUILD_BUG_ON(sizeof(union afs_dirent) != 32);
- if (test_bit(AFS_VNODE_DELETED, &AFS_FS_I(inode)->flags))
- return -ENOENT;
- return afs_open(inode, file);
- }
- /*
- * deal with one block in an AFS directory
- */
- static int afs_dir_iterate_block(unsigned *fpos,
- union afs_dir_block *block,
- unsigned blkoff,
- void *cookie,
- filldir_t filldir)
- {
- union afs_dirent *dire;
- unsigned offset, next, curr;
- size_t nlen;
- int tmp, ret;
- _enter("%u,%x,%p,,",*fpos,blkoff,block);
- curr = (*fpos - blkoff) / sizeof(union afs_dirent);
- /* walk through the block, an entry at a time */
- for (offset = AFS_DIRENT_PER_BLOCK - block->pagehdr.nentries;
-