aboutsummaryrefslogtreecommitdiff
path: root/fs/hfsplus/dir.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/hfsplus/dir.c')
-rw-r--r--fs/hfsplus/dir.c226
1 files changed, 152 insertions, 74 deletions
diff --git a/fs/hfsplus/dir.c b/fs/hfsplus/dir.c
index 9d59c0571f5..610a3260bef 100644
--- a/fs/hfsplus/dir.c
+++ b/fs/hfsplus/dir.c
@@ -12,9 +12,12 @@
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/random.h>
+#include <linux/nls.h>
#include "hfsplus_fs.h"
#include "hfsplus_raw.h"
+#include "xattr.h"
+#include "acl.h"
static inline void hfsplus_instantiate(struct dentry *dentry,
struct inode *inode, u32 cnid)
@@ -25,7 +28,7 @@ static inline void hfsplus_instantiate(struct dentry *dentry,
/* Find the entry inside dir named dentry->d_name */
static struct dentry *hfsplus_lookup(struct inode *dir, struct dentry *dentry,
- struct nameidata *nd)
+ unsigned int flags)
{
struct inode *inode = NULL;
struct hfs_find_data fd;
@@ -37,9 +40,10 @@ static struct dentry *hfsplus_lookup(struct inode *dir, struct dentry *dentry,
sb = dir->i_sb;
- dentry->d_op = &hfsplus_dentry_operations;
dentry->d_fsdata = NULL;
- hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd);
+ err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd);
+ if (err)
+ return ERR_PTR(err);
hfsplus_cat_build_key(sb, fd.search_key, dir->i_ino, &dentry->d_name);
again:
err = hfs_brec_read(&fd, &entry, sizeof(entry));
@@ -66,11 +70,17 @@ again:
goto fail;
}
cnid = be32_to_cpu(entry.file.id);
- if (entry.file.user_info.fdType == cpu_to_be32(HFSP_HARDLINK_TYPE) &&
- entry.file.user_info.fdCreator == cpu_to_be32(HFSP_HFSPLUS_CREATOR) &&
- (entry.file.create_date == HFSPLUS_I(HFSPLUS_SB(sb)->hidden_dir)->create_date ||
- entry.file.create_date == HFSPLUS_I(sb->s_root->d_inode)->create_date) &&
- HFSPLUS_SB(sb)->hidden_dir) {
+ if (entry.file.user_info.fdType ==
+ cpu_to_be32(HFSP_HARDLINK_TYPE) &&
+ entry.file.user_info.fdCreator ==
+ cpu_to_be32(HFSP_HFSPLUS_CREATOR) &&
+ (entry.file.create_date ==
+ HFSPLUS_I(HFSPLUS_SB(sb)->hidden_dir)->
+ create_date ||
+ entry.file.create_date ==
+ HFSPLUS_I(sb->s_root->d_inode)->
+ create_date) &&
+ HFSPLUS_SB(sb)->hidden_dir) {
struct qstr str;
char name[32];
@@ -83,17 +93,19 @@ again:
linkid = 0;
} else {
dentry->d_fsdata = (void *)(unsigned long)cnid;
- linkid = be32_to_cpu(entry.file.permissions.dev);
+ linkid =
+ be32_to_cpu(entry.file.permissions.dev);
str.len = sprintf(name, "iNode%d", linkid);
str.name = name;
hfsplus_cat_build_key(sb, fd.search_key,
- HFSPLUS_SB(sb)->hidden_dir->i_ino, &str);
+ HFSPLUS_SB(sb)->hidden_dir->i_ino,
+ &str);
goto again;
}
} else if (!dentry->d_fsdata)
dentry->d_fsdata = (void *)(unsigned long)cnid;
} else {
- printk(KERN_ERR "hfs: invalid catalog entry type in lookup\n");
+ pr_err("invalid catalog entry type in lookup\n");
err = -EIO;
goto fail;
}
@@ -111,73 +123,90 @@ fail:
return ERR_PTR(err);
}
-static int hfsplus_readdir(struct file *filp, void *dirent, filldir_t filldir)
+static int hfsplus_readdir(struct file *file, struct dir_context *ctx)
{
- struct inode *inode = filp->f_path.dentry->d_inode;
+ struct inode *inode = file_inode(file);
struct super_block *sb = inode->i_sb;
int len, err;
- char strbuf[HFSPLUS_MAX_STRLEN + 1];
+ char *strbuf;
hfsplus_cat_entry entry;
struct hfs_find_data fd;
struct hfsplus_readdir_data *rd;
u16 type;
- if (filp->f_pos >= inode->i_size)
+ if (file->f_pos >= inode->i_size)
return 0;
- hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd);
+ err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd);
+ if (err)
+ return err;
+ strbuf = kmalloc(NLS_MAX_CHARSET_SIZE * HFSPLUS_MAX_STRLEN + 1, GFP_KERNEL);
+ if (!strbuf) {
+ err = -ENOMEM;
+ goto out;
+ }
hfsplus_cat_build_key(sb, fd.search_key, inode->i_ino, NULL);
- err = hfs_brec_find(&fd);
+ err = hfs_brec_find(&fd, hfs_find_rec_by_key);
if (err)
goto out;
- switch ((u32)filp->f_pos) {
- case 0:
+ if (ctx->pos == 0) {
/* This is completely artificial... */
- if (filldir(dirent, ".", 1, 0, inode->i_ino, DT_DIR))
+ if (!dir_emit_dot(file, ctx))
goto out;
- filp->f_pos++;
- /* fall through */
- case 1:
- hfs_bnode_read(fd.bnode, &entry, fd.entryoffset, fd.entrylength);
+ ctx->pos = 1;
+ }
+ if (ctx->pos == 1) {
+ if (fd.entrylength > sizeof(entry) || fd.entrylength < 0) {
+ err = -EIO;
+ goto out;
+ }
+
+ hfs_bnode_read(fd.bnode, &entry, fd.entryoffset,
+ fd.entrylength);
if (be16_to_cpu(entry.type) != HFSPLUS_FOLDER_THREAD) {
- printk(KERN_ERR "hfs: bad catalog folder thread\n");
+ pr_err("bad catalog folder thread\n");
err = -EIO;
goto out;
}
if (fd.entrylength < HFSPLUS_MIN_THREAD_SZ) {
- printk(KERN_ERR "hfs: truncated catalog thread\n");
+ pr_err("truncated catalog thread\n");
err = -EIO;
goto out;
}
- if (filldir(dirent, "..", 2, 1,
+ if (!dir_emit(ctx, "..", 2,
be32_to_cpu(entry.thread.parentID), DT_DIR))
goto out;
- filp->f_pos++;
- /* fall through */
- default:
- if (filp->f_pos >= inode->i_size)
- goto out;
- err = hfs_brec_goto(&fd, filp->f_pos - 1);
- if (err)
- goto out;
+ ctx->pos = 2;
}
-
+ if (ctx->pos >= inode->i_size)
+ goto out;
+ err = hfs_brec_goto(&fd, ctx->pos - 1);
+ if (err)
+ goto out;
for (;;) {
if (be32_to_cpu(fd.key->cat.parent) != inode->i_ino) {
- printk(KERN_ERR "hfs: walked past end of dir\n");
+ pr_err("walked past end of dir\n");
+ err = -EIO;
+ goto out;
+ }
+
+ if (fd.entrylength > sizeof(entry) || fd.entrylength < 0) {
err = -EIO;
goto out;
}
- hfs_bnode_read(fd.bnode, &entry, fd.entryoffset, fd.entrylength);
+
+ hfs_bnode_read(fd.bnode, &entry, fd.entryoffset,
+ fd.entrylength);
type = be16_to_cpu(entry.type);
- len = HFSPLUS_MAX_STRLEN;
+ len = NLS_MAX_CHARSET_SIZE * HFSPLUS_MAX_STRLEN;
err = hfsplus_uni2asc(sb, &fd.key->cat.name, strbuf, &len);
if (err)
goto out;
if (type == HFSPLUS_FOLDER) {
- if (fd.entrylength < sizeof(struct hfsplus_cat_folder)) {
- printk(KERN_ERR "hfs: small dir entry\n");
+ if (fd.entrylength <
+ sizeof(struct hfsplus_cat_folder)) {
+ pr_err("small dir entry\n");
err = -EIO;
goto out;
}
@@ -185,44 +214,63 @@ static int hfsplus_readdir(struct file *filp, void *dirent, filldir_t filldir)
HFSPLUS_SB(sb)->hidden_dir->i_ino ==
be32_to_cpu(entry.folder.id))
goto next;
- if (filldir(dirent, strbuf, len, filp->f_pos,
+ if (!dir_emit(ctx, strbuf, len,
be32_to_cpu(entry.folder.id), DT_DIR))
break;
} else if (type == HFSPLUS_FILE) {
+ u16 mode;
+ unsigned type = DT_UNKNOWN;
+
if (fd.entrylength < sizeof(struct hfsplus_cat_file)) {
- printk(KERN_ERR "hfs: small file entry\n");
+ pr_err("small file entry\n");
err = -EIO;
goto out;
}
- if (filldir(dirent, strbuf, len, filp->f_pos,
- be32_to_cpu(entry.file.id), DT_REG))
+
+ mode = be16_to_cpu(entry.file.permissions.mode);
+ if (S_ISREG(mode))
+ type = DT_REG;
+ else if (S_ISLNK(mode))
+ type = DT_LNK;
+ else if (S_ISFIFO(mode))
+ type = DT_FIFO;
+ else if (S_ISCHR(mode))
+ type = DT_CHR;
+ else if (S_ISBLK(mode))
+ type = DT_BLK;
+ else if (S_ISSOCK(mode))
+ type = DT_SOCK;
+
+ if (!dir_emit(ctx, strbuf, len,
+ be32_to_cpu(entry.file.id), type))
break;
} else {
- printk(KERN_ERR "hfs: bad catalog entry type\n");
+ pr_err("bad catalog entry type\n");
err = -EIO;
goto out;
}
- next:
- filp->f_pos++;
- if (filp->f_pos >= inode->i_size)
+next:
+ ctx->pos++;
+ if (ctx->pos >= inode->i_size)
goto out;
err = hfs_brec_goto(&fd, 1);
if (err)
goto out;
}
- rd = filp->private_data;
+ rd = file->private_data;
if (!rd) {
rd = kmalloc(sizeof(struct hfsplus_readdir_data), GFP_KERNEL);
if (!rd) {
err = -ENOMEM;
goto out;
}
- filp->private_data = rd;
- rd->file = filp;
+ file->private_data = rd;
+ rd->file = file;
list_add(&rd->list, &HFSPLUS_I(inode)->open_dir_list);
}
memcpy(&rd->key, fd.key, sizeof(struct hfsplus_cat_key));
out:
+ kfree(strbuf);
hfs_find_exit(&fd);
return err;
}
@@ -273,7 +321,8 @@ static int hfsplus_link(struct dentry *src_dentry, struct inode *dst_dir,
HFSPLUS_I(inode)->linkid = id;
cnid = sbi->next_cnid++;
src_dentry->d_fsdata = (void *)(unsigned long)cnid;
- res = hfsplus_create_cat(cnid, src_dir, &src_dentry->d_name, inode);
+ res = hfsplus_create_cat(cnid, src_dir,
+ &src_dentry->d_name, inode);
if (res)
/* panic? */
goto out;
@@ -290,7 +339,7 @@ static int hfsplus_link(struct dentry *src_dentry, struct inode *dst_dir,
inode->i_ctime = CURRENT_TIME_SEC;
mark_inode_dirty(inode);
sbi->file_count++;
- dst_dir->i_sb->s_dirt = 1;
+ hfsplus_mark_mdb_dirty(dst_dir->i_sb);
out:
mutex_unlock(&sbi->vh_mutex);
return res;
@@ -395,12 +444,21 @@ static int hfsplus_symlink(struct inode *dir, struct dentry *dentry,
if (res)
goto out_err;
+ res = hfsplus_init_inode_security(inode, dir, &dentry->d_name);
+ if (res == -EOPNOTSUPP)
+ res = 0; /* Operation is not supported. */
+ else if (res) {
+ /* Try to delete anyway without error analysis. */
+ hfsplus_delete_cat(inode->i_ino, dir, &dentry->d_name);
+ goto out_err;
+ }
+
hfsplus_instantiate(dentry, inode, inode->i_ino);
mark_inode_dirty(inode);
goto out;
out_err:
- inode->i_nlink = 0;
+ clear_nlink(inode);
hfsplus_delete_inode(inode);
iput(inode);
out:
@@ -409,7 +467,7 @@ out:
}
static int hfsplus_mknod(struct inode *dir, struct dentry *dentry,
- int mode, dev_t rdev)
+ umode_t mode, dev_t rdev)
{
struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb);
struct inode *inode;
@@ -424,27 +482,38 @@ static int hfsplus_mknod(struct inode *dir, struct dentry *dentry,
init_special_inode(inode, mode, rdev);
res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode);
- if (res) {
- inode->i_nlink = 0;
- hfsplus_delete_inode(inode);
- iput(inode);
- goto out;
+ if (res)
+ goto failed_mknod;
+
+ res = hfsplus_init_inode_security(inode, dir, &dentry->d_name);
+ if (res == -EOPNOTSUPP)
+ res = 0; /* Operation is not supported. */
+ else if (res) {
+ /* Try to delete anyway without error analysis. */
+ hfsplus_delete_cat(inode->i_ino, dir, &dentry->d_name);
+ goto failed_mknod;
}
hfsplus_instantiate(dentry, inode, inode->i_ino);
mark_inode_dirty(inode);
+ goto out;
+
+failed_mknod:
+ clear_nlink(inode);
+ hfsplus_delete_inode(inode);
+ iput(inode);
out:
mutex_unlock(&sbi->vh_mutex);
return res;
}
-static int hfsplus_create(struct inode *dir, struct dentry *dentry, int mode,
- struct nameidata *nd)
+static int hfsplus_create(struct inode *dir, struct dentry *dentry, umode_t mode,
+ bool excl)
{
return hfsplus_mknod(dir, dentry, mode, 0);
}
-static int hfsplus_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+static int hfsplus_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
{
return hfsplus_mknod(dir, dentry, mode | S_IFDIR, 0);
}
@@ -473,20 +542,29 @@ static int hfsplus_rename(struct inode *old_dir, struct dentry *old_dentry,
}
const struct inode_operations hfsplus_dir_inode_operations = {
- .lookup = hfsplus_lookup,
- .create = hfsplus_create,
- .link = hfsplus_link,
- .unlink = hfsplus_unlink,
- .mkdir = hfsplus_mkdir,
- .rmdir = hfsplus_rmdir,
- .symlink = hfsplus_symlink,
- .mknod = hfsplus_mknod,
- .rename = hfsplus_rename,
+ .lookup = hfsplus_lookup,
+ .create = hfsplus_create,
+ .link = hfsplus_link,
+ .unlink = hfsplus_unlink,
+ .mkdir = hfsplus_mkdir,
+ .rmdir = hfsplus_rmdir,
+ .symlink = hfsplus_symlink,
+ .mknod = hfsplus_mknod,
+ .rename = hfsplus_rename,
+ .setxattr = generic_setxattr,
+ .getxattr = generic_getxattr,
+ .listxattr = hfsplus_listxattr,
+ .removexattr = generic_removexattr,
+#ifdef CONFIG_HFSPLUS_FS_POSIX_ACL
+ .get_acl = hfsplus_get_posix_acl,
+ .set_acl = hfsplus_set_posix_acl,
+#endif
};
const struct file_operations hfsplus_dir_operations = {
+ .fsync = hfsplus_file_fsync,
.read = generic_read_dir,
- .readdir = hfsplus_readdir,
+ .iterate = hfsplus_readdir,
.unlocked_ioctl = hfsplus_ioctl,
.llseek = generic_file_llseek,
.release = hfsplus_dir_release,