📄 dir.c
字号:
/* * dir.c * * Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke * */#include <linux/sched.h>#include <linux/errno.h>#include <linux/stat.h>#include <linux/kernel.h>#include <linux/malloc.h>#include <linux/mm.h>#include <linux/smb_fs.h>#include <linux/smbno.h>#include <asm/segment.h>#include <asm/semaphore.h>#include <linux/errno.h>static int smb_dir_read(struct inode *inode, struct file *filp, char *buf, int count);static int smb_readdir(struct inode *inode, struct file *filp, void *dirent, filldir_t filldir);static struct smb_inode_info * smb_find_dir_inode(struct inode *parent, const char *name, int len);static int smb_lookup(struct inode *dir, const char *__name, int len, struct inode **result);static int smb_create(struct inode *dir, const char *name, int len, int mode, struct inode **result);static int smb_mkdir(struct inode *dir, const char *name, int len, int mode);static int smb_rmdir(struct inode *dir, const char *name, int len);static int smb_unlink(struct inode *dir, const char *name, int len);static int smb_rename(struct inode *old_dir, const char *old_name, int old_len, struct inode *new_dir, const char *new_name, int new_len, int must_be_dir);static struct file_operations smb_dir_operations ={ NULL, /* lseek - default */ smb_dir_read, /* read - bad */ NULL, /* write - bad */ smb_readdir, /* readdir */ NULL, /* select - default */ smb_ioctl, /* ioctl - default */ NULL, /* mmap */ NULL, /* no special open code */ NULL, /* no special release code */ NULL /* fsync */};struct inode_operations smb_dir_inode_operations ={ &smb_dir_operations, /* default directory file ops */ smb_create, /* create */ smb_lookup, /* lookup */ NULL, /* link */ smb_unlink, /* unlink */ NULL, /* symlink */ smb_mkdir, /* mkdir */ smb_rmdir, /* rmdir */ NULL, /* mknod */ smb_rename, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ NULL, /* truncate */ NULL, /* permission */ NULL /* smap */};static intstrncasecmp(const char *s1, const char *s2, int len){ int result = 0; for (; len > 0; len -= 1) { char c1, c2; c1 = (*s1 >= 'a' && *s1 <= 'z') ? *s1 - ('a' - 'A') : *s1; c2 = (*s2 >= 'a' && *s2 <= 'z') ? *s2 - ('a' - 'A') : *s2; s1 += 1; s2 += 1; if ((result = c1 - c2) != 0 || c1 == 0) { return result; } } return result;}struct smb_inode_info *smb_find_inode(struct smb_server *server, ino_t ino){ struct smb_inode_info *root = &(server->root); struct smb_inode_info *this = root; do { if (ino == smb_info_ino(this)) { return this; } this = this->next; } while (this != root); return NULL;}static ino_tsmb_fresh_inodes(struct smb_server *server, int no){ static ino_t seed = 1; struct smb_inode_info *root = &(server->root); struct smb_inode_info *this; retry: if (seed + no <= no) { /* avoid inode number of 0 at wrap-around */ seed += no; } this = root; do { /* We assume that ino_t is unsigned! */ if (this->finfo.f_ino - seed < no) { seed += no; goto retry; } this = this->next; } while (this != root); seed += no; return seed - no;}static intsmb_dir_read(struct inode *inode, struct file *filp, char *buf, int count){ return -EISDIR;}static unsigned long c_ino = 0;static kdev_t c_dev;static int c_size;static int c_seen_eof;static int c_last_returned_index;static struct smb_dirent *c_entry = NULL;static struct smb_dirent *smb_search_in_cache(struct inode *dir, unsigned long f_pos){ int i; if ((dir->i_dev != c_dev) || (dir->i_ino != c_ino)) { return NULL; } for (i = 0; i < c_size; i++) { if (f_pos == c_entry[i].f_pos) { c_last_returned_index = i; return &(c_entry[i]); } } return NULL;}static intsmb_refill_dir_cache(struct smb_server *server, struct inode *dir, unsigned long f_pos){ int result; static struct semaphore sem = MUTEX; int i; ino_t ino; do { down(&sem); result = smb_proc_readdir(server, dir, f_pos, SMB_READDIR_CACHE_SIZE, c_entry); if (result <= 0) { smb_invalid_dir_cache(dir->i_ino); up(&sem); return result; } c_seen_eof = (result < SMB_READDIR_CACHE_SIZE); c_dev = dir->i_dev; c_ino = dir->i_ino; c_size = result; c_last_returned_index = 0; ino = smb_fresh_inodes(server, c_size); for (i = 0; i < c_size; i++) { c_entry[i].f_ino = ino; ino += 1; } up(&sem); } while ((c_dev != dir->i_dev) || (c_ino != dir->i_ino)); return result;}static intsmb_readdir(struct inode *dir, struct file *filp, void *dirent, filldir_t filldir){ int result, i = 0; struct smb_dirent *entry = NULL; struct smb_server *server = SMB_SERVER(dir); DPRINTK("smb_readdir: filp->f_pos = %d\n", (int) filp->f_pos); DDPRINTK("smb_readdir: dir->i_ino = %ld, c_ino = %ld\n", dir->i_ino, c_ino); if ((dir == NULL) || !S_ISDIR(dir->i_mode)) { printk("smb_readdir: dir is NULL or not a directory\n"); return -EBADF; } if (c_entry == NULL) { i = sizeof(struct smb_dirent) * SMB_READDIR_CACHE_SIZE; c_entry = (struct smb_dirent *) smb_vmalloc(i); if (c_entry == NULL) { printk("smb_readdir: no MEMORY for cache\n"); return -ENOMEM; } } if (filp->f_pos == 0) { c_ino = 0; c_dev = 0; c_seen_eof = 0; if (filldir(dirent, ".", 1, filp->f_pos, smb_info_ino(SMB_INOP(dir))) < 0) { return 0; } filp->f_pos += 1; } if (filp->f_pos == 1) { if (filldir(dirent, "..", 2, filp->f_pos, smb_info_ino(SMB_INOP(dir)->dir)) < 0) { return 0; } filp->f_pos += 1; } entry = smb_search_in_cache(dir, filp->f_pos); if (entry == NULL) { if (c_seen_eof) { /* End of directory */ return 0; } result = smb_refill_dir_cache(server, dir, filp->f_pos); if (result <= 0) { return result; } entry = c_entry; } while (entry < &(c_entry[c_size])) { /* We found it. For getwd(), we have to return the correct inode in d_ino if the inode is currently in use. Otherwise the inode number does not matter. (You can argue a lot about this..) */ struct smb_inode_info *ino_info = smb_find_dir_inode(dir, entry->name, entry->len); ino_t ino = entry->f_ino; if (ino_info != NULL) { ino = smb_info_ino(ino_info); } DDPRINTK("smb_readdir: entry->name = %s\n", entry->name); DDPRINTK("smb_readdir: entry->f_pos = %ld\n", entry->f_pos); if (filldir(dirent, entry->name, strlen(entry->name), entry->f_pos, ino) < 0) { break; } if ((dir->i_dev != c_dev) || (dir->i_ino != c_ino) || (entry->f_pos != filp->f_pos)) { /* Someone has destroyed the cache while we slept in filldir */ break; } filp->f_pos += 1; entry += 1; } return 0;}voidsmb_init_dir_cache(void){ c_ino = 0; c_dev = 0; c_entry = NULL;}voidsmb_invalid_dir_cache(unsigned long ino){ /* TODO: check for dev as well */ if (ino == c_ino) { c_ino = 0; c_seen_eof = 0; }}voidsmb_free_dir_cache(void){ if (c_entry != NULL) { smb_vfree(c_entry); } c_entry = NULL;}/* Insert a NEW smb_inode_info into the inode tree of our filesystem, under dir. The caller must assure that it's not already there. We assume that path is allocated for us. */static struct inode *smb_iget(struct inode *dir, struct smb_inode_info *new_inode_info){ struct inode *inode; struct smb_inode_info *root; if ((dir == NULL) || (new_inode_info == NULL)) { printk("smb_iget: parameter is NULL\n"); return NULL; } new_inode_info->state = SMB_INODE_LOOKED_UP; new_inode_info->nused = 0; new_inode_info->dir = SMB_INOP(dir); SMB_INOP(dir)->nused += 1; /* * We have to link the new inode_info into the doubly linked * list of inode_infos to make a complete linear search possible. */ root = &(SMB_SERVER(dir)->root); new_inode_info->prev = root; new_inode_info->next = root->next; root->next->prev = new_inode_info; root->next = new_inode_info; if (!(inode = iget(dir->i_sb, smb_info_ino(new_inode_info)))) { printk("smb_iget: iget failed!"); /* * If we blocked in iget(), another task may have referenced * the info structure ... clean up with smb_free_inode_info. */ smb_free_inode_info(new_inode_info); return NULL; } return inode;}voidsmb_free_inode_info(struct smb_inode_info *i){ if (i == NULL) { printk("smb_free_inode: i == NULL\n"); return; } i->state = SMB_INODE_CACHED; while ((i->nused == 0) && (i->state == SMB_INODE_CACHED)) { struct smb_inode_info *dir = i->dir; i->next->prev = i->prev; i->prev->next = i->next; smb_kfree_s(i, sizeof(struct smb_inode_info)); if (dir == NULL)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -