📄 emd.c
字号:
/* * linux/fs/umsdos/emd.c * * Written 1993 by Jacques Gelinas * * Extended MS-DOS directory handling functions */#include <linux/types.h>#include <linux/fcntl.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/errno.h>#include <linux/string.h>#include <linux/msdos_fs.h>#include <linux/umsdos_fs.h>#include <linux/dcache.h>#include <linux/pagemap.h>#include <asm/delay.h>static void copy_entry(struct umsdos_dirent *p, struct umsdos_dirent *q){ p->name_len = q->name_len; p->name[p->name_len]='\0'; p->flags = q->flags; p->nlink = le16_to_cpu (q->nlink); /* FIXME -- 32bit UID/GID issues */ p->uid = le16_to_cpu (q->uid); p->gid = le16_to_cpu (q->gid); p->atime = le32_to_cpu (q->atime); p->mtime = le32_to_cpu (q->mtime); p->ctime = le32_to_cpu (q->ctime); p->rdev = le16_to_cpu (q->rdev); p->mode = le16_to_cpu (q->mode);}/* * Lookup the EMD dentry for a directory. * * Note: the caller must hold a lock on the parent directory. */struct dentry *umsdos_get_emd_dentry(struct dentry *parent){ struct dentry *demd; demd = umsdos_lookup_dentry(parent, UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN, 1); return demd;}/* * Check whether a directory has an EMD file. * * Note: the caller must hold a lock on the parent directory. */int umsdos_have_emd(struct dentry *dir){ struct dentry *demd = umsdos_get_emd_dentry (dir); int found = 0; if (!IS_ERR(demd)) { if (demd->d_inode) found = 1; dput(demd); } return found;}/* * Create the EMD file for a directory if it doesn't * already exist. Returns 0 or an error code. * * Note: the caller must hold a lock on the parent directory. */int umsdos_make_emd(struct dentry *parent){ struct dentry *demd = umsdos_get_emd_dentry(parent); int err = PTR_ERR(demd); if (IS_ERR(demd)) { printk("umsdos_make_emd: can't get dentry in %s, err=%d\n", parent->d_name.name, err); goto out; } /* already created? */ err = 0; if (demd->d_inode) goto out_set;Printk(("umsdos_make_emd: creating EMD %s/%s\n",parent->d_name.name, demd->d_name.name)); err = msdos_create(parent->d_inode, demd, S_IFREG | 0777); if (err) { printk (KERN_WARNING "umsdos_make_emd: create %s/%s failed, err=%d\n", parent->d_name.name, demd->d_name.name, err); }out_set: dput(demd);out: return err;}/* * Read an entry from the EMD file. * Support variable length record. * Return -EIO if error, 0 if OK. * * does not change {d,i}_count */int umsdos_emd_dir_readentry (struct dentry *demd, loff_t *pos, struct umsdos_dirent *entry){ struct address_space *mapping = demd->d_inode->i_mapping; struct page *page; struct umsdos_dirent *p; int offs = *pos & ~PAGE_CACHE_MASK; int recsize; int ret = 0; page = read_cache_page(mapping, *pos>>PAGE_CACHE_SHIFT, (filler_t*)mapping->a_ops->readpage, NULL); if (IS_ERR(page)) goto sync_fail; wait_on_page(page); if (!Page_Uptodate(page)) goto async_fail; p = (struct umsdos_dirent*)(kmap(page)+offs); /* if this is an invalid entry (invalid name length), ignore it */ if( p->name_len > UMSDOS_MAXNAME ) { printk (KERN_WARNING "Ignoring invalid EMD entry with size %d\n", entry->name_len); p->name_len = 0; ret = -ENAMETOOLONG; /* notify umssync(8) code that something is wrong */ } recsize = umsdos_evalrecsize(p->name_len); if (offs + recsize > PAGE_CACHE_SIZE) { struct page *page2; int part = (char *)(page_address(page) + PAGE_CACHE_SIZE) - p->spare; page2 = read_cache_page(mapping, 1+(*pos>>PAGE_CACHE_SHIFT), (filler_t*)mapping->a_ops->readpage, NULL); if (IS_ERR(page2)) { kunmap(page); page_cache_release(page); page = page2; goto sync_fail; } wait_on_page(page2); if (!Page_Uptodate(page2)) { kunmap(page); page_cache_release(page2); goto async_fail; } memcpy(entry->spare,p->spare,part); memcpy(entry->spare+part,kmap(page2), recsize+offs-PAGE_CACHE_SIZE); kunmap(page2); page_cache_release(page2); } else memcpy(entry->spare,p->spare,((char*)p+recsize)-p->spare); copy_entry(entry, p); kunmap(page); page_cache_release(page); *pos += recsize; return ret;async_fail: page_cache_release(page); page = ERR_PTR(-EIO);sync_fail: return PTR_ERR(page);}/* * Write an entry in the EMD file. * Return 0 if OK, -EIO if some error. * * Note: the caller must hold a lock on the parent directory. */int umsdos_writeentry (struct dentry *parent, struct umsdos_info *info, int free_entry){ struct inode *dir = parent->d_inode; struct umsdos_dirent *entry = &info->entry; struct dentry *emd_dentry; int ret; struct umsdos_dirent entry0,*p; struct address_space *mapping; struct page *page, *page2 = NULL; int offs; emd_dentry = umsdos_get_emd_dentry(parent); ret = PTR_ERR(emd_dentry); if (IS_ERR(emd_dentry)) goto out; /* make sure there's an EMD file */ ret = -EIO; if (!emd_dentry->d_inode) { printk(KERN_WARNING "umsdos_writeentry: no EMD file in %s/%s\n", parent->d_parent->d_name.name, parent->d_name.name); goto out_dput; } if (free_entry) { /* #Specification: EMD file / empty entries * Unused entries in the EMD file are identified * by the name_len field equal to 0. However to * help future extension (or bug correction :-( ), * empty entries are filled with 0. */ memset (&entry0, 0, sizeof (entry0)); entry = &entry0; } else if (entry->name_len > 0) { memset (entry->name + entry->name_len, '\0', sizeof (entry->name) - entry->name_len); /* #Specification: EMD file / spare bytes * 10 bytes are unused in each record of the EMD. They * are set to 0 all the time, so it will be possible * to do new stuff and rely on the state of those * bytes in old EMD files. */ memset (entry->spare, 0, sizeof (entry->spare)); } /* write the entry and update the parent timestamps */ mapping = emd_dentry->d_inode->i_mapping; offs = info->f_pos & ~PAGE_CACHE_MASK; ret = -ENOMEM; page = grab_cache_page(mapping, info->f_pos>>PAGE_CACHE_SHIFT); if (!page) goto out_dput; p = (struct umsdos_dirent *) (page_address(page) + offs); if (offs + info->recsize > PAGE_CACHE_SIZE) { ret = mapping->a_ops->prepare_write(NULL,page,offs, PAGE_CACHE_SIZE); if (ret) goto out_unlock; page2 = grab_cache_page(mapping, (info->f_pos>>PAGE_CACHE_SHIFT)+1); if (!page2) goto out_unlock2; ret = mapping->a_ops->prepare_write(NULL,page2,0, offs+info->recsize-PAGE_CACHE_SIZE); if (ret) goto out_unlock3; p->name_len = entry->name_len; p->flags = entry->flags; p->nlink = cpu_to_le16(entry->nlink); p->uid = cpu_to_le16(entry->uid); p->gid = cpu_to_le16(entry->gid); p->atime = cpu_to_le32(entry->atime); p->mtime = cpu_to_le32(entry->mtime); p->ctime = cpu_to_le32(entry->ctime); p->rdev = cpu_to_le16(entry->rdev); p->mode = cpu_to_le16(entry->mode); memcpy(p->name,entry->name, (char *)(page_address(page) + PAGE_CACHE_SIZE) - p->spare); memcpy(page_address(page2), entry->spare+PAGE_CACHE_SIZE-offs, offs+info->recsize-PAGE_CACHE_SIZE); ret = mapping->a_ops->commit_write(NULL,page2,0, offs+info->recsize-PAGE_CACHE_SIZE); if (ret) goto out_unlock3; ret = mapping->a_ops->commit_write(NULL,page,offs, PAGE_CACHE_SIZE); UnlockPage(page2); page_cache_release(page2); if (ret) goto out_unlock; } else { ret = mapping->a_ops->prepare_write(NULL,page,offs, offs + info->recsize); if (ret) goto out_unlock; p->name_len = entry->name_len; p->flags = entry->flags; p->nlink = cpu_to_le16(entry->nlink); p->uid = cpu_to_le16(entry->uid); p->gid = cpu_to_le16(entry->gid); p->atime = cpu_to_le32(entry->atime); p->mtime = cpu_to_le32(entry->mtime); p->ctime = cpu_to_le32(entry->ctime); p->rdev = cpu_to_le16(entry->rdev); p->mode = cpu_to_le16(entry->mode); memcpy(p->spare,entry->spare,((char*)p+info->recsize)-p->spare); ret = mapping->a_ops->commit_write(NULL,page,offs, offs + info->recsize); if (ret) goto out_unlock; } UnlockPage(page); page_cache_release(page); dir->i_ctime = dir->i_mtime = CURRENT_TIME; mark_inode_dirty(dir);out_dput: dput(emd_dentry);out: Printk (("umsdos_writeentry /mn/: returning %d...\n", ret)); return ret;out_unlock3: UnlockPage(page2); page_cache_release(page2);out_unlock2: ClearPageUptodate(page); kunmap(page);out_unlock: UnlockPage(page); page_cache_release(page); printk ("UMSDOS: problem with EMD file: can't write\n"); goto out_dput;}/* * General search, locate a name in the EMD file or an empty slot to * store it. if info->entry.name_len == 0, search the first empty * slot (of the proper size). * * Return 0 if found, -ENOENT if not found, another error code if * other problem. * * So this routine is used to either find an existing entry or to * create a new one, while making sure it is a new one. After you * get -ENOENT, you make sure the entry is stuffed correctly and
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -