?? dir.c
字號(hào):
EXPORT_SYMBOL_GPL(fat_search_long);struct fat_ioctl_filldir_callback { void __user *dirent; int result; /* for dir ioctl */ const char *longname; int long_len; const char *shortname; int short_len;};static int __fat_readdir(struct inode *inode, struct file *filp, void *dirent, filldir_t filldir, int short_only, int both){ struct super_block *sb = inode->i_sb; struct msdos_sb_info *sbi = MSDOS_SB(sb); struct buffer_head *bh; struct msdos_dir_entry *de; struct nls_table *nls_disk = sbi->nls_disk; unsigned char nr_slots; wchar_t bufuname[14]; wchar_t *unicode = NULL; unsigned char c, work[MSDOS_NAME]; unsigned char bufname[FAT_MAX_SHORT_SIZE], *ptname = bufname; unsigned short opt_shortname = sbi->options.shortname; int isvfat = sbi->options.isvfat; int nocase = sbi->options.nocase; const char *fill_name = NULL; unsigned long inum; unsigned long lpos, dummy, *furrfu = &lpos; loff_t cpos; int chi, chl, i, i2, j, last, last_u, dotoffset = 0, fill_len = 0; int ret = 0; lock_super(sb); cpos = filp->f_pos; /* Fake . and .. for the root directory. */ if (inode->i_ino == MSDOS_ROOT_INO) { while (cpos < 2) { if (filldir(dirent, "..", cpos+1, cpos, MSDOS_ROOT_INO, DT_DIR) < 0) goto out; cpos++; filp->f_pos++; } if (cpos == 2) { dummy = 2; furrfu = &dummy; cpos = 0; } } if (cpos & (sizeof(struct msdos_dir_entry) - 1)) { ret = -ENOENT; goto out; } bh = NULL;get_new: if (fat_get_entry(inode, &cpos, &bh, &de) == -1) goto end_of_dir;parse_record: nr_slots = 0; /* * Check for long filename entry, but if short_only, we don't * need to parse long filename. */ if (isvfat && !short_only) { if (de->name[0] == DELETED_FLAG) goto record_end; if (de->attr != ATTR_EXT && (de->attr & ATTR_VOLUME)) goto record_end; if (de->attr != ATTR_EXT && IS_FREE(de->name)) goto record_end; } else { if ((de->attr & ATTR_VOLUME) || IS_FREE(de->name)) goto record_end; } if (isvfat && de->attr == ATTR_EXT) { int status = fat_parse_long(inode, &cpos, &bh, &de, &unicode, &nr_slots); if (status < 0) { filp->f_pos = cpos; ret = status; goto out; } else if (status == PARSE_INVALID) goto record_end; else if (status == PARSE_NOT_LONGNAME) goto parse_record; else if (status == PARSE_EOF) goto end_of_dir; if (nr_slots) { void *longname = unicode + FAT_MAX_UNI_CHARS; int size = PATH_MAX - FAT_MAX_UNI_SIZE; int len = fat_uni_to_x8(sbi, unicode, longname, size); fill_name = longname; fill_len = len; /* !both && !short_only, so we don't need shortname. */ if (!both) goto start_filldir; } } if (sbi->options.dotsOK) { ptname = bufname; dotoffset = 0; if (de->attr & ATTR_HIDDEN) { *ptname++ = '.'; dotoffset = 1; } } memcpy(work, de->name, sizeof(de->name)); /* see namei.c, msdos_format_name */ if (work[0] == 0x05) work[0] = 0xE5; for (i = 0, j = 0, last = 0, last_u = 0; i < 8;) { if (!(c = work[i])) break; chl = fat_shortname2uni(nls_disk, &work[i], 8 - i, &bufuname[j++], opt_shortname, de->lcase & CASE_LOWER_BASE); if (chl <= 1) { ptname[i++] = (!nocase && c>='A' && c<='Z') ? c+32 : c; if (c != ' ') { last = i; last_u = j; } } else { last_u = j; for (chi = 0; chi < chl && i < 8; chi++) { ptname[i] = work[i]; i++; last = i; } } } i = last; j = last_u; fat_short2uni(nls_disk, ".", 1, &bufuname[j++]); ptname[i++] = '.'; for (i2 = 8; i2 < MSDOS_NAME;) { if (!(c = work[i2])) break; chl = fat_shortname2uni(nls_disk, &work[i2], MSDOS_NAME - i2, &bufuname[j++], opt_shortname, de->lcase & CASE_LOWER_EXT); if (chl <= 1) { i2++; ptname[i++] = (!nocase && c>='A' && c<='Z') ? c+32 : c; if (c != ' ') { last = i; last_u = j; } } else { last_u = j; for (chi = 0; chi < chl && i2 < MSDOS_NAME; chi++) { ptname[i++] = work[i2++]; last = i; } } } if (!last) goto record_end; i = last + dotoffset; j = last_u; if (isvfat) { bufuname[j] = 0x0000; i = fat_uni_to_x8(sbi, bufuname, bufname, sizeof(bufname)); } if (nr_slots) { /* hack for fat_ioctl_filldir() */ struct fat_ioctl_filldir_callback *p = dirent; p->longname = fill_name; p->long_len = fill_len; p->shortname = bufname; p->short_len = i; fill_name = NULL; fill_len = 0; } else { fill_name = bufname; fill_len = i; }start_filldir: lpos = cpos - (nr_slots + 1) * sizeof(struct msdos_dir_entry); if (!memcmp(de->name, MSDOS_DOT, MSDOS_NAME)) inum = inode->i_ino; else if (!memcmp(de->name, MSDOS_DOTDOT, MSDOS_NAME)) { inum = parent_ino(filp->f_path.dentry); } else { loff_t i_pos = fat_make_i_pos(sb, bh, de); struct inode *tmp = fat_iget(sb, i_pos); if (tmp) { inum = tmp->i_ino; iput(tmp); } else inum = iunique(sb, MSDOS_ROOT_INO); } if (filldir(dirent, fill_name, fill_len, *furrfu, inum, (de->attr & ATTR_DIR) ? DT_DIR : DT_REG) < 0) goto fill_failed;record_end: furrfu = &lpos; filp->f_pos = cpos; goto get_new;end_of_dir: filp->f_pos = cpos;fill_failed: brelse(bh); if (unicode) __putname(unicode);out: unlock_super(sb); return ret;}static int fat_readdir(struct file *filp, void *dirent, filldir_t filldir){ struct inode *inode = filp->f_path.dentry->d_inode; return __fat_readdir(inode, filp, dirent, filldir, 0, 0);}#define FAT_IOCTL_FILLDIR_FUNC(func, dirent_type) \static int func(void *__buf, const char *name, int name_len, \ loff_t offset, u64 ino, unsigned int d_type) \{ \ struct fat_ioctl_filldir_callback *buf = __buf; \ struct dirent_type __user *d1 = buf->dirent; \ struct dirent_type __user *d2 = d1 + 1; \ \ if (buf->result) \ return -EINVAL; \ buf->result++; \ \ if (name != NULL) { \ /* dirent has only short name */ \ if (name_len >= sizeof(d1->d_name)) \ name_len = sizeof(d1->d_name) - 1; \ \ if (put_user(0, d2->d_name) || \ put_user(0, &d2->d_reclen) || \ copy_to_user(d1->d_name, name, name_len) || \ put_user(0, d1->d_name + name_len) || \ put_user(name_len, &d1->d_reclen)) \ goto efault; \ } else { \ /* dirent has short and long name */ \ const char *longname = buf->longname; \ int long_len = buf->long_len; \ const char *shortname = buf->shortname; \ int short_len = buf->short_len; \ \ if (long_len >= sizeof(d1->d_name)) \ long_len = sizeof(d1->d_name) - 1; \ if (short_len >= sizeof(d1->d_name)) \ short_len = sizeof(d1->d_name) - 1; \ \ if (copy_to_user(d2->d_name, longname, long_len) || \ put_user(0, d2->d_name + long_len) || \ put_user(long_len, &d2->d_reclen) || \ put_user(ino, &d2->d_ino) || \ put_user(offset, &d2->d_off) || \ copy_to_user(d1->d_name, shortname, short_len) || \ put_user(0, d1->d_name + short_len) || \ put_user(short_len, &d1->d_reclen)) \ goto efault; \ } \ return 0; \efault: \ buf->result = -EFAULT; \ return -EFAULT; \}FAT_IOCTL_FILLDIR_FUNC(fat_ioctl_filldir, __fat_dirent)static int fat_ioctl_readdir(struct inode *inode, struct file *filp, void __user *dirent, filldir_t filldir, int short_only, int both){ struct fat_ioctl_filldir_callback buf; int ret; buf.dirent = dirent; buf.result = 0; mutex_lock(&inode->i_mutex); ret = -ENOENT; if (!IS_DEADDIR(inode)) { ret = __fat_readdir(inode, filp, &buf, filldir, short_only, both); } mutex_unlock(&inode->i_mutex); if (ret >= 0) ret = buf.result; return ret;}static int fat_dir_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg){ struct __fat_dirent __user *d1 = (struct __fat_dirent __user *)arg; int short_only, both; switch (cmd) { case VFAT_IOCTL_READDIR_SHORT: short_only = 1; both = 0; break; case VFAT_IOCTL_READDIR_BOTH: short_only = 0; both = 1; break; default: return fat_generic_ioctl(inode, filp, cmd, arg); } if (!access_ok(VERIFY_WRITE, d1, sizeof(struct __fat_dirent[2]))) return -EFAULT; /* * Yes, we don't need this put_user() absolutely. However old * code didn't return the right value. So, app use this value, * in order to check whether it is EOF. */ if (put_user(0, &d1->d_reclen)) return -EFAULT; return fat_ioctl_readdir(inode, filp, d1, fat_ioctl_filldir, short_only, both);}#ifdef CONFIG_COMPAT#define VFAT_IOCTL_READDIR_BOTH32 _IOR('r', 1, struct compat_dirent[2])#define VFAT_IOCTL_READDIR_SHORT32 _IOR('r', 2, struct compat_dirent[2])FAT_IOCTL_FILLDIR_FUNC(fat_compat_ioctl_filldir, compat_dirent)static long fat_compat_dir_ioctl(struct file *filp, unsigned cmd, unsigned long arg){ struct inode *inode = filp->f_path.dentry->d_inode; struct compat_dirent __user *d1 = compat_ptr(arg); int short_only, both; switch (cmd) { case VFAT_IOCTL_READDIR_SHORT32: short_only = 1; both = 0; break; case VFAT_IOCTL_READDIR_BOTH32: short_only = 0; both = 1; break; default: return -ENOIOCTLCMD; } if (!access_ok(VERIFY_WRITE, d1, sizeof(struct compat_dirent[2]))) return -EFAULT; /* * Yes, we don't need this put_user() absolutely. However old * code didn't return the right value. So, app use this value, * in order to check whether it is EOF. */ if (put_user(0, &d1->d_reclen)) return -EFAULT; return fat_ioctl_readdir(inode, filp, d1, fat_compat_ioctl_filldir, short_only, both);}#endif /* CONFIG_COMPAT */const struct file_operations fat_dir_operations = { .llseek = generic_file_llseek, .read = generic_read_dir, .readdir = fat_readdir, .ioctl = fat_dir_ioctl,#ifdef CONFIG_COMPAT .compat_ioctl = fat_compat_dir_ioctl,#endif .fsync = file_fsync,};static int fat_get_short_entry(struct inode *dir, loff_t *pos, struct buffer_head **bh, struct msdos_dir_entry **de){ while (fat_get_entry(dir, pos, bh, de) >= 0) { /* free entry or long name entry or volume label */ if (!IS_FREE((*de)->name) && !((*de)->attr & ATTR_VOLUME)) return 0; } return -ENOENT;}/* * The ".." entry can not provide the "struct fat_slot_info" informations * for inode. So, this function provide the some informations only. */int fat_get_dotdot_entry(struct inode *dir, struct buffer_head **bh, struct msdos_dir_entry **de, loff_t *i_pos){ loff_t offset; offset = 0; *bh = NULL; while (fat_get_short_entry(dir, &offset, bh, de) >= 0) { if (!strncmp((*de)->name, MSDOS_DOTDOT, MSDOS_NAME)) { *i_pos = fat_make_i_pos(dir->i_sb, *bh, *de); return 0; } } return -ENOENT;}EXPORT_SYMBOL_GPL(fat_get_dotdot_entry);/* See if directory is empty */int fat_dir_empty(struct inode *dir){ struct buffer_head *bh; struct msdos_dir_entry *de; loff_t cpos; int result = 0; bh = NULL; cpos = 0; while (fat_get_short_entry(dir, &cpos, &bh, &de) >= 0) { if (strncmp(de->name, MSDOS_DOT , MSDOS_NAME) && strncmp(de->name, MSDOS_DOTDOT, MSDOS_NAME)) { result = -ENOTEMPTY; break; } } brelse(bh); return result;}EXPORT_SYMBOL_GPL(fat_dir_empty);/* * fat_subdirs counts the number of sub-directories of dir. It can be run * on directories being created. */int fat_subdirs(struct inode *dir){ struct buffer_head *bh; struct msdos_dir_entry *de;
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -