/* 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,
unsigned int flags);
static int afs_dir_open(struct inode *inode, struct file *file);
static int afs_readdir(struct file *file, struct dir_context *ctx);
static int afs_d_revalidate(struct dentry *dentry, unsigned int flags);
static int afs_d_delete(const struct dentry *dentry);
static void afs_d_release(struct dentry *dentry);
static int afs_lookup_filldir(struct dir_context *ctx, const char *name, int nlen,
loff_t fpos, u64 ino, unsigned dtype);
static int afs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
bool excl);
static int afs_mkdir(struct inode *dir, struct dentry *dentry, umode_t 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,
.iterate = 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 dir_context ctx;
struct afs_fid fid;
struct qstr name;
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(