* Short name translation 1999, 2001 by Wolfram Pienkoss <wp@bszh.de>
*/
+#include <linux/module.h>
#include <linux/slab.h>
#include <linux/time.h>
#include <linux/msdos_fs.h>
#include <linux/dirent.h>
#include <linux/smp_lock.h>
#include <linux/buffer_head.h>
-
#include <asm/uaccess.h>
-struct file_operations fat_dir_operations = {
- .read = generic_read_dir,
- .readdir = fat_readdir,
- .ioctl = fat_dir_ioctl,
- .fsync = file_fsync,
-};
-
/*
* Convert Unicode 16 to UTF8, translated Unicode, or ASCII.
* If uni_xlate is enabled and we can't get a 1:1 conversion, use a
* but ignore that right now.
* Ahem... Stack smashing in ring 0 isn't fun. Fixed.
*/
-static int
-uni16_to_x8(unsigned char *ascii, wchar_t *uni, int uni_xlate,
- struct nls_table *nls)
+static int uni16_to_x8(unsigned char *ascii, wchar_t *uni, int uni_xlate,
+ struct nls_table *nls)
{
wchar_t *ip, ec;
unsigned char *op, nc;
return (op - ascii);
}
-#if 0
-static void dump_de(struct msdos_dir_entry *de)
-{
- int i;
- unsigned char *p = (unsigned char *) de;
- printk("[");
-
- for (i = 0; i < 32; i++, p++) {
- printk("%02x ", *p);
- }
- printk("]\n");
-}
-#endif
-
static inline int
fat_short2uni(struct nls_table *t, unsigned char *c, int clen, wchar_t *uni)
{
charlen = 1;
} else if (charlen <= 1) {
unsigned char nc = t->charset2lower[*c];
-
+
if (!nc)
nc = *c;
-
+
if ( (charlen = t->char2uni(&nc, 1, uni)) < 0) {
*uni = 0x003f; /* a question mark */
charlen = 1;
}
} else
*uni = wc;
-
+
return charlen;
}
else if (opt & VFAT_SFN_DISPLAY_WINNT) {
if (lower)
len = fat_short2lower_uni(nls, buf, buf_size, uni_buf);
- else
+ else
len = fat_short2uni(nls, buf, buf_size, uni_buf);
} else
len = fat_short2uni(nls, buf, buf_size, uni_buf);
return res;
}
+EXPORT_SYMBOL(fat_search_long);
+
+struct fat_ioctl_filldir_callback {
+ struct dirent __user *dirent;
+ int result;
+ /* for dir ioctl */
+ const char *longname;
+ int long_len;
+ const char *shortname;
+ int short_len;
+};
+
static int fat_readdirx(struct inode *inode, struct file *filp, void *dirent,
- filldir_t filldir, int shortnames, int both)
+ filldir_t filldir, int short_only, int both)
{
struct super_block *sb = inode->i_sb;
struct buffer_head *bh;
struct msdos_dir_entry *de;
struct nls_table *nls_io = MSDOS_SB(sb)->nls_io;
struct nls_table *nls_disk = MSDOS_SB(sb)->nls_disk;
- wchar_t bufuname[14];
unsigned char long_slots;
+ const char *fill_name;
+ int fill_len;
+ wchar_t bufuname[14];
wchar_t *unicode = NULL;
unsigned char c, work[8], bufname[56], *ptname = bufname;
unsigned long lpos, dummy, *furrfu = &lpos;
int chi, chl, i, i2, j, last, last_u, dotoffset = 0;
loff_t i_pos, cpos;
int ret = 0;
-
+
lock_kernel();
cpos = filp->f_pos;
goto out;
}
- bh = NULL;
+ bh = NULL;
GetNew:
long_slots = 0;
if (fat_get_entry(inode,&cpos,&bh,&de,&i_pos) == -1)
unsigned char alias_checksum;
if (!unicode) {
- unicode = (wchar_t *)
- __get_free_page(GFP_KERNEL);
+ unicode = (wchar_t *)__get_free_page(GFP_KERNEL);
if (!unicode) {
filp->f_pos = cpos;
brelse(bh);
: uni16_to_x8(bufname, bufuname, uni_xlate, nls_io);
}
- if (!long_slots||shortnames) {
- if (both)
- bufname[i] = '\0';
- if (filldir(dirent, bufname, i, *furrfu, inum,
- (de->attr & ATTR_DIR) ? DT_DIR : DT_REG) < 0)
- goto FillFailed;
- } else {
- unsigned char longname[275];
+ fill_name = bufname;
+ fill_len = i;
+ if (!short_only && long_slots) {
+ /* convert the unicode long name. 261 is maximum size
+ * of unicode buffer. (13 * slots + nul) */
+ void *longname = unicode + 261;
+ int buf_size = PAGE_SIZE - (261 * sizeof(unicode[0]));
int long_len = utf8
- ? utf8_wcstombs(longname, unicode, sizeof(longname))
- : uni16_to_x8(longname, unicode, uni_xlate,
- nls_io);
- if (both) {
- memcpy(&longname[long_len+1], bufname, i);
- long_len += i;
+ ? utf8_wcstombs(longname, unicode, buf_size)
+ : uni16_to_x8(longname, unicode, uni_xlate, nls_io);
+
+ if (!both) {
+ fill_name = longname;
+ fill_len = long_len;
+ } else {
+ /* hack for fat_ioctl_filldir() */
+ struct fat_ioctl_filldir_callback *p = dirent;
+
+ p->longname = longname;
+ p->long_len = long_len;
+ p->shortname = bufname;
+ p->short_len = i;
+ fill_name = NULL;
+ fill_len = 0;
}
- if (filldir(dirent, longname, long_len, *furrfu, inum,
- (de->attr & ATTR_DIR) ? DT_DIR : DT_REG) < 0)
- goto FillFailed;
}
+ if (filldir(dirent, fill_name, fill_len, *furrfu, inum,
+ (de->attr & ATTR_DIR) ? DT_DIR : DT_REG) < 0)
+ goto FillFailed;
RecEnd:
furrfu = &lpos;
FillFailed:
if (bh)
brelse(bh);
- if (unicode) {
- free_page((unsigned long) unicode);
- }
+ if (unicode)
+ free_page((unsigned long)unicode);
out:
unlock_kernel();
return ret;
}
-int fat_readdir(struct file *filp, void *dirent, filldir_t filldir)
+static int fat_readdir(struct file *filp, void *dirent, filldir_t filldir)
{
struct inode *inode = filp->f_dentry->d_inode;
return fat_readdirx(inode, filp, dirent, filldir, 0, 0);
}
-struct fat_ioctl_filldir_callback {
- struct dirent __user *dirent;
- int result;
-};
-
-static int fat_ioctl_filldir(void *__buf, const char * name, int name_len,
+static int fat_ioctl_filldir(void *__buf, const char *name, int name_len,
loff_t offset, ino_t ino, unsigned int d_type)
{
struct fat_ioctl_filldir_callback *buf = __buf;
struct dirent __user *d1 = buf->dirent;
struct dirent __user *d2 = d1 + 1;
- int len, slen;
- int dotdir;
if (buf->result)
return -EINVAL;
buf->result++;
- if ((name_len == 1 && name[0] == '.') ||
- (name_len == 2 && name[0] == '.' && name[1] == '.')) {
- dotdir = 1;
- len = name_len;
- } else {
- dotdir = 0;
- len = strlen(name);
- }
- if (len != name_len) {
- slen = name_len - len;
- if (copy_to_user(d2->d_name, name, len) ||
- put_user(0, d2->d_name + len) ||
- put_user(len, &d2->d_reclen) ||
- put_user(ino, &d2->d_ino) ||
- put_user(offset, &d2->d_off) ||
- copy_to_user(d1->d_name, name+len+1, slen) ||
- put_user(0, d1->d_name+slen) ||
- put_user(slen, &d1->d_reclen))
- goto efault;
- } else {
+ 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, len) ||
- put_user(0, d1->d_name+len) ||
- put_user(len, &d1->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;
return -EFAULT;
}
-int fat_dir_ioctl(struct inode * inode, struct file * filp,
+static int fat_dir_ioctl(struct inode * inode, struct file * filp,
unsigned int cmd, unsigned long arg)
{
struct fat_ioctl_filldir_callback buf;
struct dirent __user *d1;
- int ret, shortname, both;
+ int ret, short_only, both;
switch (cmd) {
case VFAT_IOCTL_READDIR_SHORT:
- shortname = 1;
- both = 1;
+ short_only = 1;
+ both = 0;
break;
case VFAT_IOCTL_READDIR_BOTH:
- shortname = 0;
+ short_only = 0;
both = 1;
break;
default:
ret = -ENOENT;
if (!IS_DEADDIR(inode)) {
ret = fat_readdirx(inode, filp, &buf, fat_ioctl_filldir,
- shortname, both);
+ short_only, both);
}
up(&inode->i_sem);
if (ret >= 0)
return ret;
}
-/* This assumes that size of cluster is above the 32*slots */
-
-int fat_add_entries(struct inode *dir,int slots, struct buffer_head **bh,
- struct msdos_dir_entry **de, loff_t *i_pos)
-{
- struct super_block *sb = dir->i_sb;
- loff_t offset, curr;
- int row;
- struct buffer_head *new_bh;
-
- offset = curr = 0;
- *bh = NULL;
- row = 0;
- while (fat_get_entry(dir, &curr, bh, de, i_pos) > -1) {
- /* check the maximum size of directory */
- if (curr >= FAT_MAX_DIR_SIZE) {
- brelse(*bh);
- return -ENOSPC;
- }
-
- if (IS_FREE((*de)->name)) {
- if (++row == slots)
- return offset;
- } else {
- row = 0;
- offset = curr;
- }
- }
- if ((dir->i_ino == MSDOS_ROOT_INO) && (MSDOS_SB(sb)->fat_bits != 32))
- return -ENOSPC;
- new_bh = fat_extend_dir(dir);
- if (IS_ERR(new_bh))
- return PTR_ERR(new_bh);
- brelse(new_bh);
- do {
- fat_get_entry(dir, &curr, bh, de, i_pos);
- } while (++row < slots);
-
- return offset;
-}
-
-int fat_new_dir(struct inode *dir, struct inode *parent, int is_vfat)
-{
- struct buffer_head *bh;
- struct msdos_dir_entry *de;
- __le16 date, time;
-
- bh = fat_extend_dir(dir);
- if (IS_ERR(bh))
- return PTR_ERR(bh);
-
- /* zeroed out, so... */
- fat_date_unix2dos(dir->i_mtime.tv_sec,&time,&date);
- de = (struct msdos_dir_entry*)&bh->b_data[0];
- memcpy(de[0].name,MSDOS_DOT,MSDOS_NAME);
- memcpy(de[1].name,MSDOS_DOTDOT,MSDOS_NAME);
- de[0].attr = de[1].attr = ATTR_DIR;
- de[0].time = de[1].time = time;
- de[0].date = de[1].date = date;
- if (is_vfat) { /* extra timestamps */
- de[0].ctime = de[1].ctime = time;
- de[0].adate = de[0].cdate =
- de[1].adate = de[1].cdate = date;
- }
- de[0].start = CT_LE_W(MSDOS_I(dir)->i_logstart);
- de[0].starthi = CT_LE_W(MSDOS_I(dir)->i_logstart>>16);
- de[1].start = CT_LE_W(MSDOS_I(parent)->i_logstart);
- de[1].starthi = CT_LE_W(MSDOS_I(parent)->i_logstart>>16);
- mark_buffer_dirty(bh);
- brelse(bh);
- dir->i_atime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
- mark_inode_dirty(dir);
-
- return 0;
-}
+struct file_operations fat_dir_operations = {
+ .read = generic_read_dir,
+ .readdir = fat_readdir,
+ .ioctl = fat_dir_ioctl,
+ .fsync = file_fsync,
+};
static int fat_get_short_entry(struct inode *dir, loff_t *pos,
struct buffer_head **bh,
return result;
}
+EXPORT_SYMBOL(fat_dir_empty);
+
/*
* fat_subdirs counts the number of sub-directories of dir. It can be run
* on directories being created.
}
return -ENOENT;
}
+
+EXPORT_SYMBOL(fat_scan);
+
+static struct buffer_head *fat_extend_dir(struct inode *inode)
+{
+ struct super_block *sb = inode->i_sb;
+ struct buffer_head *bh, *res = NULL;
+ int nr, sec_per_clus = MSDOS_SB(sb)->sec_per_clus;
+ sector_t sector, last_sector;
+
+ if (MSDOS_SB(sb)->fat_bits != 32) {
+ if (inode->i_ino == MSDOS_ROOT_INO)
+ return ERR_PTR(-ENOSPC);
+ }
+
+ nr = fat_add_cluster(inode);
+ if (nr < 0)
+ return ERR_PTR(nr);
+
+ sector = fat_clus_to_blknr(MSDOS_SB(sb), nr);
+ last_sector = sector + sec_per_clus;
+ for ( ; sector < last_sector; sector++) {
+ if ((bh = sb_getblk(sb, sector))) {
+ memset(bh->b_data, 0, sb->s_blocksize);
+ set_buffer_uptodate(bh);
+ mark_buffer_dirty(bh);
+ if (!res)
+ res = bh;
+ else
+ brelse(bh);
+ }
+ }
+ if (res == NULL)
+ res = ERR_PTR(-EIO);
+ if (inode->i_size & (sb->s_blocksize - 1)) {
+ fat_fs_panic(sb, "Odd directory size");
+ inode->i_size = (inode->i_size + sb->s_blocksize)
+ & ~((loff_t)sb->s_blocksize - 1);
+ }
+ inode->i_size += MSDOS_SB(sb)->cluster_size;
+ MSDOS_I(inode)->mmu_private += MSDOS_SB(sb)->cluster_size;
+
+ return res;
+}
+
+/* This assumes that size of cluster is above the 32*slots */
+
+int fat_add_entries(struct inode *dir,int slots, struct buffer_head **bh,
+ struct msdos_dir_entry **de, loff_t *i_pos)
+{
+ struct super_block *sb = dir->i_sb;
+ loff_t offset, curr;
+ int row;
+ struct buffer_head *new_bh;
+
+ offset = curr = 0;
+ *bh = NULL;
+ row = 0;
+ while (fat_get_entry(dir, &curr, bh, de, i_pos) > -1) {
+ /* check the maximum size of directory */
+ if (curr >= FAT_MAX_DIR_SIZE) {
+ brelse(*bh);
+ return -ENOSPC;
+ }
+
+ if (IS_FREE((*de)->name)) {
+ if (++row == slots)
+ return offset;
+ } else {
+ row = 0;
+ offset = curr;
+ }
+ }
+ if ((dir->i_ino == MSDOS_ROOT_INO) && (MSDOS_SB(sb)->fat_bits != 32))
+ return -ENOSPC;
+ new_bh = fat_extend_dir(dir);
+ if (IS_ERR(new_bh))
+ return PTR_ERR(new_bh);
+ brelse(new_bh);
+ do {
+ fat_get_entry(dir, &curr, bh, de, i_pos);
+ } while (++row < slots);
+
+ return offset;
+}
+
+EXPORT_SYMBOL(fat_add_entries);
+
+int fat_new_dir(struct inode *dir, struct inode *parent, int is_vfat)
+{
+ struct buffer_head *bh;
+ struct msdos_dir_entry *de;
+ __le16 date, time;
+
+ bh = fat_extend_dir(dir);
+ if (IS_ERR(bh))
+ return PTR_ERR(bh);
+
+ /* zeroed out, so... */
+ fat_date_unix2dos(dir->i_mtime.tv_sec,&time,&date);
+ de = (struct msdos_dir_entry*)&bh->b_data[0];
+ memcpy(de[0].name,MSDOS_DOT,MSDOS_NAME);
+ memcpy(de[1].name,MSDOS_DOTDOT,MSDOS_NAME);
+ de[0].attr = de[1].attr = ATTR_DIR;
+ de[0].time = de[1].time = time;
+ de[0].date = de[1].date = date;
+ if (is_vfat) { /* extra timestamps */
+ de[0].ctime = de[1].ctime = time;
+ de[0].adate = de[0].cdate =
+ de[1].adate = de[1].cdate = date;
+ }
+ de[0].start = cpu_to_le16(MSDOS_I(dir)->i_logstart);
+ de[0].starthi = cpu_to_le16(MSDOS_I(dir)->i_logstart>>16);
+ de[1].start = cpu_to_le16(MSDOS_I(parent)->i_logstart);
+ de[1].starthi = cpu_to_le16(MSDOS_I(parent)->i_logstart>>16);
+ mark_buffer_dirty(bh);
+ brelse(bh);
+ dir->i_atime = dir->i_ctime = dir->i_mtime = CURRENT_TIME_SEC;
+ mark_inode_dirty(dir);
+
+ return 0;
+}
+
+EXPORT_SYMBOL(fat_new_dir);