2005-04-17 05:20:36 +07:00
|
|
|
/*
|
|
|
|
* linux/fs/hfsplus/dir.c
|
|
|
|
*
|
|
|
|
* Copyright (C) 2001
|
|
|
|
* Brad Boyer (flar@allandria.com)
|
|
|
|
* (C) 2003 Ardis Technologies <roman@ardistech.com>
|
|
|
|
*
|
|
|
|
* Handling of directories
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/errno.h>
|
|
|
|
#include <linux/fs.h>
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/random.h>
|
hfsplus: fix worst-case unicode to char conversion of file names and attributes
This is a series of 3 patches which corrects issues in HFS+ concerning
the use of non-english file names and attributes. Names and attributes
are stored internally as UTF-16 units up to a fixed maximum size, and
convert to and from user-representation by NLS. The code incorrectly
assume that NLS string lengths are equal to unicode lengths, which is
only true for English ascii usage.
This patch (of 3):
The HFS Plus Volume Format specification (TN1150) states that file names
are stored internally as a maximum of 255 unicode characters, as defined
by The Unicode Standard, Version 2.0 [Unicode, Inc. ISBN
0-201-48345-9]. File names are converted by the NLS system on Linux
before presented to the user.
255 CJK characters converts to UTF-8 with 1 unicode character to up to 3
bytes, and to GB18030 with 1 unicode character to up to 4 bytes. Thus,
trying in a UTF-8 locale to list files with names of more than 85 CJK
characters results in:
$ ls /mnt
ls: reading directory /mnt: File name too long
The receiving buffer to hfsplus_uni2asc() needs to be 255 x
NLS_MAX_CHARSET_SIZE bytes, not 255 bytes as the code has always been.
Similar consideration applies to attributes, which are stored internally
as a maximum of 127 UTF-16BE units. See XNU source for an up-to-date
reference on attributes.
Strictly speaking, the maximum value of NLS_MAX_CHARSET_SIZE = 6 is not
attainable in the case of conversion to UTF-8, as going beyond 3 bytes
requires the use of surrogate pairs, i.e. consuming two input units.
Thanks Anton Altaparmakov for reviewing an earlier version of this
change.
This patch fixes all callers of hfsplus_uni2asc(), and also enables the
use of long non-English file names in HFS+. The getting and setting,
and general usage of long non-English attributes requires further
forthcoming work, in the following patches of this series.
[akpm@linux-foundation.org: fix build]
Signed-off-by: Hin-Tak Leung <htl10@users.sourceforge.net>
Reviewed-by: Anton Altaparmakov <anton@tuxera.com>
Cc: Vyacheslav Dubeyko <slava@dubeyko.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Sougata Santra <sougata@tuxera.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2014-06-07 04:36:21 +07:00
|
|
|
#include <linux/nls.h>
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
#include "hfsplus_fs.h"
|
|
|
|
#include "hfsplus_raw.h"
|
2013-02-28 08:03:04 +07:00
|
|
|
#include "xattr.h"
|
2013-09-12 04:24:30 +07:00
|
|
|
#include "acl.h"
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
static inline void hfsplus_instantiate(struct dentry *dentry,
|
|
|
|
struct inode *inode, u32 cnid)
|
|
|
|
{
|
|
|
|
dentry->d_fsdata = (void *)(unsigned long)cnid;
|
|
|
|
d_instantiate(dentry, inode);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Find the entry inside dir named dentry->d_name */
|
|
|
|
static struct dentry *hfsplus_lookup(struct inode *dir, struct dentry *dentry,
|
2012-06-11 04:13:09 +07:00
|
|
|
unsigned int flags)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
struct inode *inode = NULL;
|
|
|
|
struct hfs_find_data fd;
|
|
|
|
struct super_block *sb;
|
|
|
|
hfsplus_cat_entry entry;
|
|
|
|
int err;
|
|
|
|
u32 cnid, linkid = 0;
|
|
|
|
u16 type;
|
|
|
|
|
|
|
|
sb = dir->i_sb;
|
2007-07-16 13:41:23 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
dentry->d_fsdata = NULL;
|
2011-07-06 05:29:59 +07:00
|
|
|
err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd);
|
|
|
|
if (err)
|
|
|
|
return ERR_PTR(err);
|
2014-12-19 07:17:12 +07:00
|
|
|
err = hfsplus_cat_build_key(sb, fd.search_key, dir->i_ino,
|
|
|
|
&dentry->d_name);
|
|
|
|
if (unlikely(err < 0))
|
|
|
|
goto fail;
|
2005-04-17 05:20:36 +07:00
|
|
|
again:
|
|
|
|
err = hfs_brec_read(&fd, &entry, sizeof(entry));
|
|
|
|
if (err) {
|
|
|
|
if (err == -ENOENT) {
|
|
|
|
hfs_find_exit(&fd);
|
|
|
|
/* No such entry */
|
|
|
|
inode = NULL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
type = be16_to_cpu(entry.type);
|
|
|
|
if (type == HFSPLUS_FOLDER) {
|
|
|
|
if (fd.entrylength < sizeof(struct hfsplus_cat_folder)) {
|
|
|
|
err = -EIO;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
cnid = be32_to_cpu(entry.folder.id);
|
|
|
|
dentry->d_fsdata = (void *)(unsigned long)cnid;
|
|
|
|
} else if (type == HFSPLUS_FILE) {
|
|
|
|
if (fd.entrylength < sizeof(struct hfsplus_cat_file)) {
|
|
|
|
err = -EIO;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
cnid = be32_to_cpu(entry.file.id);
|
2010-12-16 23:08:38 +07:00
|
|
|
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 ==
|
2015-03-18 05:25:59 +07:00
|
|
|
HFSPLUS_I(d_inode(sb->s_root))->
|
2010-12-16 23:08:38 +07:00
|
|
|
create_date) &&
|
|
|
|
HFSPLUS_SB(sb)->hidden_dir) {
|
2005-04-17 05:20:36 +07:00
|
|
|
struct qstr str;
|
|
|
|
char name[32];
|
|
|
|
|
|
|
|
if (dentry->d_fsdata) {
|
2006-01-19 08:43:10 +07:00
|
|
|
/*
|
|
|
|
* We found a link pointing to another link,
|
|
|
|
* so ignore it and treat it as regular file.
|
|
|
|
*/
|
|
|
|
cnid = (unsigned long)dentry->d_fsdata;
|
|
|
|
linkid = 0;
|
|
|
|
} else {
|
|
|
|
dentry->d_fsdata = (void *)(unsigned long)cnid;
|
2010-12-16 23:08:38 +07:00
|
|
|
linkid =
|
|
|
|
be32_to_cpu(entry.file.permissions.dev);
|
2006-01-19 08:43:10 +07:00
|
|
|
str.len = sprintf(name, "iNode%d", linkid);
|
|
|
|
str.name = name;
|
2014-12-19 07:17:12 +07:00
|
|
|
err = hfsplus_cat_build_key(sb, fd.search_key,
|
2010-12-16 23:08:38 +07:00
|
|
|
HFSPLUS_SB(sb)->hidden_dir->i_ino,
|
|
|
|
&str);
|
2014-12-19 07:17:12 +07:00
|
|
|
if (unlikely(err < 0))
|
|
|
|
goto fail;
|
2006-01-19 08:43:10 +07:00
|
|
|
goto again;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
} else if (!dentry->d_fsdata)
|
|
|
|
dentry->d_fsdata = (void *)(unsigned long)cnid;
|
|
|
|
} else {
|
2013-05-01 05:27:55 +07:00
|
|
|
pr_err("invalid catalog entry type in lookup\n");
|
2005-04-17 05:20:36 +07:00
|
|
|
err = -EIO;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
hfs_find_exit(&fd);
|
2008-02-07 15:15:40 +07:00
|
|
|
inode = hfsplus_iget(dir->i_sb, cnid);
|
|
|
|
if (IS_ERR(inode))
|
|
|
|
return ERR_CAST(inode);
|
2005-04-17 05:20:36 +07:00
|
|
|
if (S_ISREG(inode->i_mode))
|
2010-10-14 20:54:28 +07:00
|
|
|
HFSPLUS_I(inode)->linkid = linkid;
|
2005-04-17 05:20:36 +07:00
|
|
|
out:
|
|
|
|
d_add(dentry, inode);
|
|
|
|
return NULL;
|
|
|
|
fail:
|
|
|
|
hfs_find_exit(&fd);
|
|
|
|
return ERR_PTR(err);
|
|
|
|
}
|
|
|
|
|
2013-05-23 01:59:39 +07:00
|
|
|
static int hfsplus_readdir(struct file *file, struct dir_context *ctx)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2013-05-23 01:59:39 +07:00
|
|
|
struct inode *inode = file_inode(file);
|
2005-04-17 05:20:36 +07:00
|
|
|
struct super_block *sb = inode->i_sb;
|
|
|
|
int len, err;
|
hfsplus: fix worst-case unicode to char conversion of file names and attributes
This is a series of 3 patches which corrects issues in HFS+ concerning
the use of non-english file names and attributes. Names and attributes
are stored internally as UTF-16 units up to a fixed maximum size, and
convert to and from user-representation by NLS. The code incorrectly
assume that NLS string lengths are equal to unicode lengths, which is
only true for English ascii usage.
This patch (of 3):
The HFS Plus Volume Format specification (TN1150) states that file names
are stored internally as a maximum of 255 unicode characters, as defined
by The Unicode Standard, Version 2.0 [Unicode, Inc. ISBN
0-201-48345-9]. File names are converted by the NLS system on Linux
before presented to the user.
255 CJK characters converts to UTF-8 with 1 unicode character to up to 3
bytes, and to GB18030 with 1 unicode character to up to 4 bytes. Thus,
trying in a UTF-8 locale to list files with names of more than 85 CJK
characters results in:
$ ls /mnt
ls: reading directory /mnt: File name too long
The receiving buffer to hfsplus_uni2asc() needs to be 255 x
NLS_MAX_CHARSET_SIZE bytes, not 255 bytes as the code has always been.
Similar consideration applies to attributes, which are stored internally
as a maximum of 127 UTF-16BE units. See XNU source for an up-to-date
reference on attributes.
Strictly speaking, the maximum value of NLS_MAX_CHARSET_SIZE = 6 is not
attainable in the case of conversion to UTF-8, as going beyond 3 bytes
requires the use of surrogate pairs, i.e. consuming two input units.
Thanks Anton Altaparmakov for reviewing an earlier version of this
change.
This patch fixes all callers of hfsplus_uni2asc(), and also enables the
use of long non-English file names in HFS+. The getting and setting,
and general usage of long non-English attributes requires further
forthcoming work, in the following patches of this series.
[akpm@linux-foundation.org: fix build]
Signed-off-by: Hin-Tak Leung <htl10@users.sourceforge.net>
Reviewed-by: Anton Altaparmakov <anton@tuxera.com>
Cc: Vyacheslav Dubeyko <slava@dubeyko.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Sougata Santra <sougata@tuxera.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2014-06-07 04:36:21 +07:00
|
|
|
char *strbuf;
|
2005-04-17 05:20:36 +07:00
|
|
|
hfsplus_cat_entry entry;
|
|
|
|
struct hfs_find_data fd;
|
|
|
|
struct hfsplus_readdir_data *rd;
|
|
|
|
u16 type;
|
|
|
|
|
2013-05-23 01:59:39 +07:00
|
|
|
if (file->f_pos >= inode->i_size)
|
2005-04-17 05:20:36 +07:00
|
|
|
return 0;
|
|
|
|
|
2011-07-06 05:29:59 +07:00
|
|
|
err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd);
|
|
|
|
if (err)
|
|
|
|
return err;
|
hfsplus: fix worst-case unicode to char conversion of file names and attributes
This is a series of 3 patches which corrects issues in HFS+ concerning
the use of non-english file names and attributes. Names and attributes
are stored internally as UTF-16 units up to a fixed maximum size, and
convert to and from user-representation by NLS. The code incorrectly
assume that NLS string lengths are equal to unicode lengths, which is
only true for English ascii usage.
This patch (of 3):
The HFS Plus Volume Format specification (TN1150) states that file names
are stored internally as a maximum of 255 unicode characters, as defined
by The Unicode Standard, Version 2.0 [Unicode, Inc. ISBN
0-201-48345-9]. File names are converted by the NLS system on Linux
before presented to the user.
255 CJK characters converts to UTF-8 with 1 unicode character to up to 3
bytes, and to GB18030 with 1 unicode character to up to 4 bytes. Thus,
trying in a UTF-8 locale to list files with names of more than 85 CJK
characters results in:
$ ls /mnt
ls: reading directory /mnt: File name too long
The receiving buffer to hfsplus_uni2asc() needs to be 255 x
NLS_MAX_CHARSET_SIZE bytes, not 255 bytes as the code has always been.
Similar consideration applies to attributes, which are stored internally
as a maximum of 127 UTF-16BE units. See XNU source for an up-to-date
reference on attributes.
Strictly speaking, the maximum value of NLS_MAX_CHARSET_SIZE = 6 is not
attainable in the case of conversion to UTF-8, as going beyond 3 bytes
requires the use of surrogate pairs, i.e. consuming two input units.
Thanks Anton Altaparmakov for reviewing an earlier version of this
change.
This patch fixes all callers of hfsplus_uni2asc(), and also enables the
use of long non-English file names in HFS+. The getting and setting,
and general usage of long non-English attributes requires further
forthcoming work, in the following patches of this series.
[akpm@linux-foundation.org: fix build]
Signed-off-by: Hin-Tak Leung <htl10@users.sourceforge.net>
Reviewed-by: Anton Altaparmakov <anton@tuxera.com>
Cc: Vyacheslav Dubeyko <slava@dubeyko.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Sougata Santra <sougata@tuxera.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2014-06-07 04:36:21 +07:00
|
|
|
strbuf = kmalloc(NLS_MAX_CHARSET_SIZE * HFSPLUS_MAX_STRLEN + 1, GFP_KERNEL);
|
|
|
|
if (!strbuf) {
|
|
|
|
err = -ENOMEM;
|
|
|
|
goto out;
|
|
|
|
}
|
2014-12-19 07:17:12 +07:00
|
|
|
hfsplus_cat_build_key_with_cnid(sb, fd.search_key, inode->i_ino);
|
2013-02-28 08:03:04 +07:00
|
|
|
err = hfs_brec_find(&fd, hfs_find_rec_by_key);
|
2005-04-17 05:20:36 +07:00
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
|
2013-05-23 01:59:39 +07:00
|
|
|
if (ctx->pos == 0) {
|
2005-04-17 05:20:36 +07:00
|
|
|
/* This is completely artificial... */
|
2013-05-23 01:59:39 +07:00
|
|
|
if (!dir_emit_dot(file, ctx))
|
2005-04-17 05:20:36 +07:00
|
|
|
goto out;
|
2013-05-23 01:59:39 +07:00
|
|
|
ctx->pos = 1;
|
|
|
|
}
|
|
|
|
if (ctx->pos == 1) {
|
2012-05-05 02:09:39 +07:00
|
|
|
if (fd.entrylength > sizeof(entry) || fd.entrylength < 0) {
|
|
|
|
err = -EIO;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2010-12-16 23:08:38 +07:00
|
|
|
hfs_bnode_read(fd.bnode, &entry, fd.entryoffset,
|
|
|
|
fd.entrylength);
|
2005-04-17 05:20:36 +07:00
|
|
|
if (be16_to_cpu(entry.type) != HFSPLUS_FOLDER_THREAD) {
|
2013-05-01 05:27:55 +07:00
|
|
|
pr_err("bad catalog folder thread\n");
|
2005-04-17 05:20:36 +07:00
|
|
|
err = -EIO;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (fd.entrylength < HFSPLUS_MIN_THREAD_SZ) {
|
2013-05-01 05:27:55 +07:00
|
|
|
pr_err("truncated catalog thread\n");
|
2005-04-17 05:20:36 +07:00
|
|
|
err = -EIO;
|
|
|
|
goto out;
|
|
|
|
}
|
2013-05-23 01:59:39 +07:00
|
|
|
if (!dir_emit(ctx, "..", 2,
|
2005-04-17 05:20:36 +07:00
|
|
|
be32_to_cpu(entry.thread.parentID), DT_DIR))
|
|
|
|
goto out;
|
2013-05-23 01:59:39 +07:00
|
|
|
ctx->pos = 2;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2013-05-23 01:59:39 +07:00
|
|
|
if (ctx->pos >= inode->i_size)
|
|
|
|
goto out;
|
|
|
|
err = hfs_brec_goto(&fd, ctx->pos - 1);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
2005-04-17 05:20:36 +07:00
|
|
|
for (;;) {
|
|
|
|
if (be32_to_cpu(fd.key->cat.parent) != inode->i_ino) {
|
2013-05-01 05:27:55 +07:00
|
|
|
pr_err("walked past end of dir\n");
|
2005-04-17 05:20:36 +07:00
|
|
|
err = -EIO;
|
|
|
|
goto out;
|
|
|
|
}
|
2012-05-05 02:09:39 +07:00
|
|
|
|
|
|
|
if (fd.entrylength > sizeof(entry) || fd.entrylength < 0) {
|
|
|
|
err = -EIO;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2010-12-16 23:08:38 +07:00
|
|
|
hfs_bnode_read(fd.bnode, &entry, fd.entryoffset,
|
|
|
|
fd.entrylength);
|
2005-04-17 05:20:36 +07:00
|
|
|
type = be16_to_cpu(entry.type);
|
hfsplus: fix worst-case unicode to char conversion of file names and attributes
This is a series of 3 patches which corrects issues in HFS+ concerning
the use of non-english file names and attributes. Names and attributes
are stored internally as UTF-16 units up to a fixed maximum size, and
convert to and from user-representation by NLS. The code incorrectly
assume that NLS string lengths are equal to unicode lengths, which is
only true for English ascii usage.
This patch (of 3):
The HFS Plus Volume Format specification (TN1150) states that file names
are stored internally as a maximum of 255 unicode characters, as defined
by The Unicode Standard, Version 2.0 [Unicode, Inc. ISBN
0-201-48345-9]. File names are converted by the NLS system on Linux
before presented to the user.
255 CJK characters converts to UTF-8 with 1 unicode character to up to 3
bytes, and to GB18030 with 1 unicode character to up to 4 bytes. Thus,
trying in a UTF-8 locale to list files with names of more than 85 CJK
characters results in:
$ ls /mnt
ls: reading directory /mnt: File name too long
The receiving buffer to hfsplus_uni2asc() needs to be 255 x
NLS_MAX_CHARSET_SIZE bytes, not 255 bytes as the code has always been.
Similar consideration applies to attributes, which are stored internally
as a maximum of 127 UTF-16BE units. See XNU source for an up-to-date
reference on attributes.
Strictly speaking, the maximum value of NLS_MAX_CHARSET_SIZE = 6 is not
attainable in the case of conversion to UTF-8, as going beyond 3 bytes
requires the use of surrogate pairs, i.e. consuming two input units.
Thanks Anton Altaparmakov for reviewing an earlier version of this
change.
This patch fixes all callers of hfsplus_uni2asc(), and also enables the
use of long non-English file names in HFS+. The getting and setting,
and general usage of long non-English attributes requires further
forthcoming work, in the following patches of this series.
[akpm@linux-foundation.org: fix build]
Signed-off-by: Hin-Tak Leung <htl10@users.sourceforge.net>
Reviewed-by: Anton Altaparmakov <anton@tuxera.com>
Cc: Vyacheslav Dubeyko <slava@dubeyko.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Sougata Santra <sougata@tuxera.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2014-06-07 04:36:21 +07:00
|
|
|
len = NLS_MAX_CHARSET_SIZE * HFSPLUS_MAX_STRLEN;
|
2005-04-17 05:20:36 +07:00
|
|
|
err = hfsplus_uni2asc(sb, &fd.key->cat.name, strbuf, &len);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
if (type == HFSPLUS_FOLDER) {
|
2010-12-16 23:08:38 +07:00
|
|
|
if (fd.entrylength <
|
|
|
|
sizeof(struct hfsplus_cat_folder)) {
|
2013-05-01 05:27:55 +07:00
|
|
|
pr_err("small dir entry\n");
|
2005-04-17 05:20:36 +07:00
|
|
|
err = -EIO;
|
|
|
|
goto out;
|
|
|
|
}
|
2010-10-01 10:42:59 +07:00
|
|
|
if (HFSPLUS_SB(sb)->hidden_dir &&
|
|
|
|
HFSPLUS_SB(sb)->hidden_dir->i_ino ==
|
|
|
|
be32_to_cpu(entry.folder.id))
|
2005-04-17 05:20:36 +07:00
|
|
|
goto next;
|
2013-05-23 01:59:39 +07:00
|
|
|
if (!dir_emit(ctx, strbuf, len,
|
2005-04-17 05:20:36 +07:00
|
|
|
be32_to_cpu(entry.folder.id), DT_DIR))
|
|
|
|
break;
|
|
|
|
} else if (type == HFSPLUS_FILE) {
|
2014-06-07 04:36:24 +07:00
|
|
|
u16 mode;
|
|
|
|
unsigned type = DT_UNKNOWN;
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
if (fd.entrylength < sizeof(struct hfsplus_cat_file)) {
|
2013-05-01 05:27:55 +07:00
|
|
|
pr_err("small file entry\n");
|
2005-04-17 05:20:36 +07:00
|
|
|
err = -EIO;
|
|
|
|
goto out;
|
|
|
|
}
|
2014-06-07 04:36:24 +07:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2013-05-23 01:59:39 +07:00
|
|
|
if (!dir_emit(ctx, strbuf, len,
|
2014-06-07 04:36:24 +07:00
|
|
|
be32_to_cpu(entry.file.id), type))
|
2005-04-17 05:20:36 +07:00
|
|
|
break;
|
|
|
|
} else {
|
2013-05-01 05:27:55 +07:00
|
|
|
pr_err("bad catalog entry type\n");
|
2005-04-17 05:20:36 +07:00
|
|
|
err = -EIO;
|
|
|
|
goto out;
|
|
|
|
}
|
2010-12-16 23:08:40 +07:00
|
|
|
next:
|
2013-05-23 01:59:39 +07:00
|
|
|
ctx->pos++;
|
|
|
|
if (ctx->pos >= inode->i_size)
|
2005-04-17 05:20:36 +07:00
|
|
|
goto out;
|
|
|
|
err = hfs_brec_goto(&fd, 1);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
}
|
2013-05-23 01:59:39 +07:00
|
|
|
rd = file->private_data;
|
2005-04-17 05:20:36 +07:00
|
|
|
if (!rd) {
|
|
|
|
rd = kmalloc(sizeof(struct hfsplus_readdir_data), GFP_KERNEL);
|
|
|
|
if (!rd) {
|
|
|
|
err = -ENOMEM;
|
|
|
|
goto out;
|
|
|
|
}
|
2013-05-23 01:59:39 +07:00
|
|
|
file->private_data = rd;
|
|
|
|
rd->file = file;
|
2016-05-13 07:02:09 +07:00
|
|
|
spin_lock(&HFSPLUS_I(inode)->open_dir_lock);
|
2010-10-01 10:43:31 +07:00
|
|
|
list_add(&rd->list, &HFSPLUS_I(inode)->open_dir_list);
|
2016-05-13 07:02:09 +07:00
|
|
|
spin_unlock(&HFSPLUS_I(inode)->open_dir_lock);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2016-05-13 07:02:09 +07:00
|
|
|
/*
|
|
|
|
* Can be done after the list insertion; exclusion with
|
|
|
|
* hfsplus_delete_cat() is provided by directory lock.
|
|
|
|
*/
|
2005-04-17 05:20:36 +07:00
|
|
|
memcpy(&rd->key, fd.key, sizeof(struct hfsplus_cat_key));
|
|
|
|
out:
|
hfsplus: fix worst-case unicode to char conversion of file names and attributes
This is a series of 3 patches which corrects issues in HFS+ concerning
the use of non-english file names and attributes. Names and attributes
are stored internally as UTF-16 units up to a fixed maximum size, and
convert to and from user-representation by NLS. The code incorrectly
assume that NLS string lengths are equal to unicode lengths, which is
only true for English ascii usage.
This patch (of 3):
The HFS Plus Volume Format specification (TN1150) states that file names
are stored internally as a maximum of 255 unicode characters, as defined
by The Unicode Standard, Version 2.0 [Unicode, Inc. ISBN
0-201-48345-9]. File names are converted by the NLS system on Linux
before presented to the user.
255 CJK characters converts to UTF-8 with 1 unicode character to up to 3
bytes, and to GB18030 with 1 unicode character to up to 4 bytes. Thus,
trying in a UTF-8 locale to list files with names of more than 85 CJK
characters results in:
$ ls /mnt
ls: reading directory /mnt: File name too long
The receiving buffer to hfsplus_uni2asc() needs to be 255 x
NLS_MAX_CHARSET_SIZE bytes, not 255 bytes as the code has always been.
Similar consideration applies to attributes, which are stored internally
as a maximum of 127 UTF-16BE units. See XNU source for an up-to-date
reference on attributes.
Strictly speaking, the maximum value of NLS_MAX_CHARSET_SIZE = 6 is not
attainable in the case of conversion to UTF-8, as going beyond 3 bytes
requires the use of surrogate pairs, i.e. consuming two input units.
Thanks Anton Altaparmakov for reviewing an earlier version of this
change.
This patch fixes all callers of hfsplus_uni2asc(), and also enables the
use of long non-English file names in HFS+. The getting and setting,
and general usage of long non-English attributes requires further
forthcoming work, in the following patches of this series.
[akpm@linux-foundation.org: fix build]
Signed-off-by: Hin-Tak Leung <htl10@users.sourceforge.net>
Reviewed-by: Anton Altaparmakov <anton@tuxera.com>
Cc: Vyacheslav Dubeyko <slava@dubeyko.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Sougata Santra <sougata@tuxera.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2014-06-07 04:36:21 +07:00
|
|
|
kfree(strbuf);
|
2005-04-17 05:20:36 +07:00
|
|
|
hfs_find_exit(&fd);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int hfsplus_dir_release(struct inode *inode, struct file *file)
|
|
|
|
{
|
|
|
|
struct hfsplus_readdir_data *rd = file->private_data;
|
|
|
|
if (rd) {
|
2016-05-13 07:02:09 +07:00
|
|
|
spin_lock(&HFSPLUS_I(inode)->open_dir_lock);
|
2005-04-17 05:20:36 +07:00
|
|
|
list_del(&rd->list);
|
2016-05-13 07:02:09 +07:00
|
|
|
spin_unlock(&HFSPLUS_I(inode)->open_dir_lock);
|
2005-04-17 05:20:36 +07:00
|
|
|
kfree(rd);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int hfsplus_link(struct dentry *src_dentry, struct inode *dst_dir,
|
|
|
|
struct dentry *dst_dentry)
|
|
|
|
{
|
2010-10-01 10:42:59 +07:00
|
|
|
struct hfsplus_sb_info *sbi = HFSPLUS_SB(dst_dir->i_sb);
|
2015-03-18 05:25:59 +07:00
|
|
|
struct inode *inode = d_inode(src_dentry);
|
|
|
|
struct inode *src_dir = d_inode(src_dentry->d_parent);
|
2005-04-17 05:20:36 +07:00
|
|
|
struct qstr str;
|
|
|
|
char name[32];
|
|
|
|
u32 cnid, id;
|
|
|
|
int res;
|
|
|
|
|
|
|
|
if (HFSPLUS_IS_RSRC(inode))
|
|
|
|
return -EPERM;
|
2010-10-14 20:54:28 +07:00
|
|
|
if (!S_ISREG(inode->i_mode))
|
|
|
|
return -EPERM;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2010-10-01 10:45:08 +07:00
|
|
|
mutex_lock(&sbi->vh_mutex);
|
2005-04-17 05:20:36 +07:00
|
|
|
if (inode->i_ino == (u32)(unsigned long)src_dentry->d_fsdata) {
|
|
|
|
for (;;) {
|
|
|
|
get_random_bytes(&id, sizeof(cnid));
|
|
|
|
id &= 0x3fffffff;
|
|
|
|
str.name = name;
|
|
|
|
str.len = sprintf(name, "iNode%d", id);
|
|
|
|
res = hfsplus_rename_cat(inode->i_ino,
|
|
|
|
src_dir, &src_dentry->d_name,
|
2010-10-01 10:42:59 +07:00
|
|
|
sbi->hidden_dir, &str);
|
2005-04-17 05:20:36 +07:00
|
|
|
if (!res)
|
|
|
|
break;
|
|
|
|
if (res != -EEXIST)
|
2010-10-01 10:45:08 +07:00
|
|
|
goto out;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2010-10-14 20:54:28 +07:00
|
|
|
HFSPLUS_I(inode)->linkid = id;
|
2010-10-01 10:42:59 +07:00
|
|
|
cnid = sbi->next_cnid++;
|
2005-04-17 05:20:36 +07:00
|
|
|
src_dentry->d_fsdata = (void *)(unsigned long)cnid;
|
2010-12-16 23:08:38 +07:00
|
|
|
res = hfsplus_create_cat(cnid, src_dir,
|
|
|
|
&src_dentry->d_name, inode);
|
2005-04-17 05:20:36 +07:00
|
|
|
if (res)
|
|
|
|
/* panic? */
|
2010-10-01 10:45:08 +07:00
|
|
|
goto out;
|
2010-10-01 10:42:59 +07:00
|
|
|
sbi->file_count++;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2010-10-01 10:42:59 +07:00
|
|
|
cnid = sbi->next_cnid++;
|
2005-04-17 05:20:36 +07:00
|
|
|
res = hfsplus_create_cat(cnid, dst_dir, &dst_dentry->d_name, inode);
|
|
|
|
if (res)
|
2010-10-01 10:45:08 +07:00
|
|
|
goto out;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-10-01 13:29:04 +07:00
|
|
|
inc_nlink(inode);
|
2005-04-17 05:20:36 +07:00
|
|
|
hfsplus_instantiate(dst_dentry, inode, cnid);
|
2010-10-23 22:11:40 +07:00
|
|
|
ihold(inode);
|
2016-09-14 21:48:05 +07:00
|
|
|
inode->i_ctime = current_time(inode);
|
2005-04-17 05:20:36 +07:00
|
|
|
mark_inode_dirty(inode);
|
2010-10-01 10:42:59 +07:00
|
|
|
sbi->file_count++;
|
2012-07-12 21:26:31 +07:00
|
|
|
hfsplus_mark_mdb_dirty(dst_dir->i_sb);
|
2010-10-01 10:45:08 +07:00
|
|
|
out:
|
|
|
|
mutex_unlock(&sbi->vh_mutex);
|
|
|
|
return res;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int hfsplus_unlink(struct inode *dir, struct dentry *dentry)
|
|
|
|
{
|
2010-10-01 10:42:59 +07:00
|
|
|
struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb);
|
2015-03-18 05:25:59 +07:00
|
|
|
struct inode *inode = d_inode(dentry);
|
2005-04-17 05:20:36 +07:00
|
|
|
struct qstr str;
|
|
|
|
char name[32];
|
|
|
|
u32 cnid;
|
|
|
|
int res;
|
|
|
|
|
|
|
|
if (HFSPLUS_IS_RSRC(inode))
|
|
|
|
return -EPERM;
|
|
|
|
|
2010-10-01 10:45:08 +07:00
|
|
|
mutex_lock(&sbi->vh_mutex);
|
2005-04-17 05:20:36 +07:00
|
|
|
cnid = (u32)(unsigned long)dentry->d_fsdata;
|
|
|
|
if (inode->i_ino == cnid &&
|
2010-10-01 10:43:31 +07:00
|
|
|
atomic_read(&HFSPLUS_I(inode)->opencnt)) {
|
2005-04-17 05:20:36 +07:00
|
|
|
str.name = name;
|
|
|
|
str.len = sprintf(name, "temp%lu", inode->i_ino);
|
|
|
|
res = hfsplus_rename_cat(inode->i_ino,
|
|
|
|
dir, &dentry->d_name,
|
2010-10-01 10:42:59 +07:00
|
|
|
sbi->hidden_dir, &str);
|
2010-10-27 18:45:50 +07:00
|
|
|
if (!res) {
|
2005-04-17 05:20:36 +07:00
|
|
|
inode->i_flags |= S_DEAD;
|
2010-10-27 18:45:50 +07:00
|
|
|
drop_nlink(inode);
|
|
|
|
}
|
2010-10-01 10:45:08 +07:00
|
|
|
goto out;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
res = hfsplus_delete_cat(cnid, dir, &dentry->d_name);
|
|
|
|
if (res)
|
2010-10-01 10:45:08 +07:00
|
|
|
goto out;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-01-19 08:43:10 +07:00
|
|
|
if (inode->i_nlink > 0)
|
2006-10-01 13:29:03 +07:00
|
|
|
drop_nlink(inode);
|
2008-04-09 22:44:07 +07:00
|
|
|
if (inode->i_ino == cnid)
|
|
|
|
clear_nlink(inode);
|
|
|
|
if (!inode->i_nlink) {
|
|
|
|
if (inode->i_ino != cnid) {
|
2010-10-01 10:42:59 +07:00
|
|
|
sbi->file_count--;
|
2010-10-01 10:43:31 +07:00
|
|
|
if (!atomic_read(&HFSPLUS_I(inode)->opencnt)) {
|
2008-04-09 22:44:07 +07:00
|
|
|
res = hfsplus_delete_cat(inode->i_ino,
|
2010-10-01 10:42:59 +07:00
|
|
|
sbi->hidden_dir,
|
2008-04-09 22:44:07 +07:00
|
|
|
NULL);
|
|
|
|
if (!res)
|
|
|
|
hfsplus_delete_inode(inode);
|
|
|
|
} else
|
|
|
|
inode->i_flags |= S_DEAD;
|
2005-04-17 05:20:36 +07:00
|
|
|
} else
|
2008-04-09 22:44:07 +07:00
|
|
|
hfsplus_delete_inode(inode);
|
2006-01-19 08:43:10 +07:00
|
|
|
} else
|
2010-10-01 10:42:59 +07:00
|
|
|
sbi->file_count--;
|
2016-09-14 21:48:05 +07:00
|
|
|
inode->i_ctime = current_time(inode);
|
2005-04-17 05:20:36 +07:00
|
|
|
mark_inode_dirty(inode);
|
2010-10-01 10:45:08 +07:00
|
|
|
out:
|
|
|
|
mutex_unlock(&sbi->vh_mutex);
|
2005-04-17 05:20:36 +07:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int hfsplus_rmdir(struct inode *dir, struct dentry *dentry)
|
|
|
|
{
|
2010-10-01 10:45:08 +07:00
|
|
|
struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb);
|
2015-03-18 05:25:59 +07:00
|
|
|
struct inode *inode = d_inode(dentry);
|
2005-04-17 05:20:36 +07:00
|
|
|
int res;
|
|
|
|
|
|
|
|
if (inode->i_size != 2)
|
|
|
|
return -ENOTEMPTY;
|
2010-10-01 10:45:08 +07:00
|
|
|
|
|
|
|
mutex_lock(&sbi->vh_mutex);
|
2005-04-17 05:20:36 +07:00
|
|
|
res = hfsplus_delete_cat(inode->i_ino, dir, &dentry->d_name);
|
|
|
|
if (res)
|
2010-10-01 10:45:08 +07:00
|
|
|
goto out;
|
2006-10-01 13:29:06 +07:00
|
|
|
clear_nlink(inode);
|
2016-09-14 21:48:05 +07:00
|
|
|
inode->i_ctime = current_time(inode);
|
2005-04-17 05:20:36 +07:00
|
|
|
hfsplus_delete_inode(inode);
|
|
|
|
mark_inode_dirty(inode);
|
2010-10-01 10:45:08 +07:00
|
|
|
out:
|
|
|
|
mutex_unlock(&sbi->vh_mutex);
|
|
|
|
return res;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int hfsplus_symlink(struct inode *dir, struct dentry *dentry,
|
|
|
|
const char *symname)
|
|
|
|
{
|
2010-10-01 10:45:08 +07:00
|
|
|
struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb);
|
2005-04-17 05:20:36 +07:00
|
|
|
struct inode *inode;
|
2015-04-17 02:47:12 +07:00
|
|
|
int res = -ENOMEM;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2010-10-01 10:45:08 +07:00
|
|
|
mutex_lock(&sbi->vh_mutex);
|
2010-10-01 10:43:54 +07:00
|
|
|
inode = hfsplus_new_inode(dir->i_sb, S_IFLNK | S_IRWXUGO);
|
2005-04-17 05:20:36 +07:00
|
|
|
if (!inode)
|
2010-10-01 10:45:08 +07:00
|
|
|
goto out;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
res = page_symlink(inode, symname, strlen(symname) + 1);
|
2010-10-01 10:43:54 +07:00
|
|
|
if (res)
|
|
|
|
goto out_err;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode);
|
2010-10-01 10:43:54 +07:00
|
|
|
if (res)
|
|
|
|
goto out_err;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2013-02-28 08:03:04 +07:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2010-10-01 10:43:54 +07:00
|
|
|
hfsplus_instantiate(dentry, inode, inode->i_ino);
|
|
|
|
mark_inode_dirty(inode);
|
2010-10-01 10:45:08 +07:00
|
|
|
goto out;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2010-10-01 10:43:54 +07:00
|
|
|
out_err:
|
2011-10-28 19:13:28 +07:00
|
|
|
clear_nlink(inode);
|
2010-10-01 10:43:54 +07:00
|
|
|
hfsplus_delete_inode(inode);
|
|
|
|
iput(inode);
|
2010-10-01 10:45:08 +07:00
|
|
|
out:
|
|
|
|
mutex_unlock(&sbi->vh_mutex);
|
2005-04-17 05:20:36 +07:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int hfsplus_mknod(struct inode *dir, struct dentry *dentry,
|
2011-07-26 12:52:52 +07:00
|
|
|
umode_t mode, dev_t rdev)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2010-10-01 10:45:08 +07:00
|
|
|
struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb);
|
2005-04-17 05:20:36 +07:00
|
|
|
struct inode *inode;
|
2015-04-17 02:47:12 +07:00
|
|
|
int res = -ENOMEM;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2010-10-01 10:45:08 +07:00
|
|
|
mutex_lock(&sbi->vh_mutex);
|
2010-10-01 10:43:50 +07:00
|
|
|
inode = hfsplus_new_inode(dir->i_sb, mode);
|
2005-04-17 05:20:36 +07:00
|
|
|
if (!inode)
|
2010-10-01 10:45:08 +07:00
|
|
|
goto out;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2010-10-14 20:54:39 +07:00
|
|
|
if (S_ISBLK(mode) || S_ISCHR(mode) || S_ISFIFO(mode) || S_ISSOCK(mode))
|
|
|
|
init_special_inode(inode, mode, rdev);
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode);
|
2013-02-28 08:03:04 +07:00
|
|
|
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;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2010-10-01 10:43:50 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
hfsplus_instantiate(dentry, inode, inode->i_ino);
|
|
|
|
mark_inode_dirty(inode);
|
2013-02-28 08:03:04 +07:00
|
|
|
goto out;
|
|
|
|
|
|
|
|
failed_mknod:
|
|
|
|
clear_nlink(inode);
|
|
|
|
hfsplus_delete_inode(inode);
|
|
|
|
iput(inode);
|
2010-10-01 10:45:08 +07:00
|
|
|
out:
|
|
|
|
mutex_unlock(&sbi->vh_mutex);
|
|
|
|
return res;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2011-07-26 12:42:34 +07:00
|
|
|
static int hfsplus_create(struct inode *dir, struct dentry *dentry, umode_t mode,
|
2012-06-11 05:05:36 +07:00
|
|
|
bool excl)
|
2010-10-01 10:43:50 +07:00
|
|
|
{
|
|
|
|
return hfsplus_mknod(dir, dentry, mode, 0);
|
|
|
|
}
|
|
|
|
|
2011-07-26 12:41:39 +07:00
|
|
|
static int hfsplus_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
|
2010-10-01 10:43:50 +07:00
|
|
|
{
|
|
|
|
return hfsplus_mknod(dir, dentry, mode | S_IFDIR, 0);
|
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
static int hfsplus_rename(struct inode *old_dir, struct dentry *old_dentry,
|
fs: support RENAME_NOREPLACE for local filesystems
This is trivial to do:
- add flags argument to foo_rename()
- check if flags doesn't have any other than RENAME_NOREPLACE
- assign foo_rename() to .rename2 instead of .rename
Filesystems converted:
affs, bfs, exofs, ext2, hfs, hfsplus, jffs2, jfs, logfs, minix, msdos,
nilfs2, omfs, reiserfs, sysvfs, ubifs, udf, ufs, vfat.
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
Acked-by: Boaz Harrosh <ooo@electrozaur.com>
Acked-by: Richard Weinberger <richard@nod.at>
Acked-by: Bob Copeland <me@bobcopeland.com>
Acked-by: Jan Kara <jack@suse.cz>
Cc: Theodore Ts'o <tytso@mit.edu>
Cc: Jaegeuk Kim <jaegeuk@kernel.org>
Cc: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
Cc: Mikulas Patocka <mpatocka@redhat.com>
Cc: David Woodhouse <dwmw2@infradead.org>
Cc: Dave Kleikamp <shaggy@kernel.org>
Cc: Ryusuke Konishi <konishi.ryusuke@lab.ntt.co.jp>
Cc: Christoph Hellwig <hch@infradead.org>
2016-09-27 16:03:57 +07:00
|
|
|
struct inode *new_dir, struct dentry *new_dentry,
|
|
|
|
unsigned int flags)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
int res;
|
|
|
|
|
fs: support RENAME_NOREPLACE for local filesystems
This is trivial to do:
- add flags argument to foo_rename()
- check if flags doesn't have any other than RENAME_NOREPLACE
- assign foo_rename() to .rename2 instead of .rename
Filesystems converted:
affs, bfs, exofs, ext2, hfs, hfsplus, jffs2, jfs, logfs, minix, msdos,
nilfs2, omfs, reiserfs, sysvfs, ubifs, udf, ufs, vfat.
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
Acked-by: Boaz Harrosh <ooo@electrozaur.com>
Acked-by: Richard Weinberger <richard@nod.at>
Acked-by: Bob Copeland <me@bobcopeland.com>
Acked-by: Jan Kara <jack@suse.cz>
Cc: Theodore Ts'o <tytso@mit.edu>
Cc: Jaegeuk Kim <jaegeuk@kernel.org>
Cc: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
Cc: Mikulas Patocka <mpatocka@redhat.com>
Cc: David Woodhouse <dwmw2@infradead.org>
Cc: Dave Kleikamp <shaggy@kernel.org>
Cc: Ryusuke Konishi <konishi.ryusuke@lab.ntt.co.jp>
Cc: Christoph Hellwig <hch@infradead.org>
2016-09-27 16:03:57 +07:00
|
|
|
if (flags & ~RENAME_NOREPLACE)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/* Unlink destination if it already exists */
|
2015-03-18 05:25:59 +07:00
|
|
|
if (d_really_is_positive(new_dentry)) {
|
VFS: (Scripted) Convert S_ISLNK/DIR/REG(dentry->d_inode) to d_is_*(dentry)
Convert the following where appropriate:
(1) S_ISLNK(dentry->d_inode) to d_is_symlink(dentry).
(2) S_ISREG(dentry->d_inode) to d_is_reg(dentry).
(3) S_ISDIR(dentry->d_inode) to d_is_dir(dentry). This is actually more
complicated than it appears as some calls should be converted to
d_can_lookup() instead. The difference is whether the directory in
question is a real dir with a ->lookup op or whether it's a fake dir with
a ->d_automount op.
In some circumstances, we can subsume checks for dentry->d_inode not being
NULL into this, provided we the code isn't in a filesystem that expects
d_inode to be NULL if the dirent really *is* negative (ie. if we're going to
use d_inode() rather than d_backing_inode() to get the inode pointer).
Note that the dentry type field may be set to something other than
DCACHE_MISS_TYPE when d_inode is NULL in the case of unionmount, where the VFS
manages the fall-through from a negative dentry to a lower layer. In such a
case, the dentry type of the negative union dentry is set to the same as the
type of the lower dentry.
However, if you know d_inode is not NULL at the call site, then you can use
the d_is_xxx() functions even in a filesystem.
There is one further complication: a 0,0 chardev dentry may be labelled
DCACHE_WHITEOUT_TYPE rather than DCACHE_SPECIAL_TYPE. Strictly, this was
intended for special directory entry types that don't have attached inodes.
The following perl+coccinelle script was used:
use strict;
my @callers;
open($fd, 'git grep -l \'S_IS[A-Z].*->d_inode\' |') ||
die "Can't grep for S_ISDIR and co. callers";
@callers = <$fd>;
close($fd);
unless (@callers) {
print "No matches\n";
exit(0);
}
my @cocci = (
'@@',
'expression E;',
'@@',
'',
'- S_ISLNK(E->d_inode->i_mode)',
'+ d_is_symlink(E)',
'',
'@@',
'expression E;',
'@@',
'',
'- S_ISDIR(E->d_inode->i_mode)',
'+ d_is_dir(E)',
'',
'@@',
'expression E;',
'@@',
'',
'- S_ISREG(E->d_inode->i_mode)',
'+ d_is_reg(E)' );
my $coccifile = "tmp.sp.cocci";
open($fd, ">$coccifile") || die $coccifile;
print($fd "$_\n") || die $coccifile foreach (@cocci);
close($fd);
foreach my $file (@callers) {
chomp $file;
print "Processing ", $file, "\n";
system("spatch", "--sp-file", $coccifile, $file, "--in-place", "--no-show-diff") == 0 ||
die "spatch failed";
}
[AV: overlayfs parts skipped]
Signed-off-by: David Howells <dhowells@redhat.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2015-01-29 19:02:35 +07:00
|
|
|
if (d_is_dir(new_dentry))
|
2010-10-01 14:12:08 +07:00
|
|
|
res = hfsplus_rmdir(new_dir, new_dentry);
|
2011-05-28 03:42:06 +07:00
|
|
|
else
|
2010-10-01 14:12:08 +07:00
|
|
|
res = hfsplus_unlink(new_dir, new_dentry);
|
2005-04-17 05:20:36 +07:00
|
|
|
if (res)
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
res = hfsplus_rename_cat((u32)(unsigned long)old_dentry->d_fsdata,
|
|
|
|
old_dir, &old_dentry->d_name,
|
|
|
|
new_dir, &new_dentry->d_name);
|
|
|
|
if (!res)
|
|
|
|
new_dentry->d_fsdata = old_dentry->d_fsdata;
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2007-02-12 15:55:39 +07:00
|
|
|
const struct inode_operations hfsplus_dir_inode_operations = {
|
2013-02-28 08:03:04 +07:00
|
|
|
.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,
|
|
|
|
.listxattr = hfsplus_listxattr,
|
2013-09-12 04:24:30 +07:00
|
|
|
#ifdef CONFIG_HFSPLUS_FS_POSIX_ACL
|
|
|
|
.get_acl = hfsplus_get_posix_acl,
|
2013-12-20 20:16:46 +07:00
|
|
|
.set_acl = hfsplus_set_posix_acl,
|
2013-09-12 04:24:30 +07:00
|
|
|
#endif
|
2005-04-17 05:20:36 +07:00
|
|
|
};
|
|
|
|
|
2006-03-28 16:56:42 +07:00
|
|
|
const struct file_operations hfsplus_dir_operations = {
|
2010-11-23 20:38:10 +07:00
|
|
|
.fsync = hfsplus_file_fsync,
|
2005-04-17 05:20:36 +07:00
|
|
|
.read = generic_read_dir,
|
2016-05-13 07:02:09 +07:00
|
|
|
.iterate_shared = hfsplus_readdir,
|
2010-04-27 21:24:20 +07:00
|
|
|
.unlocked_ioctl = hfsplus_ioctl,
|
2005-04-17 05:20:36 +07:00
|
|
|
.llseek = generic_file_llseek,
|
|
|
|
.release = hfsplus_dir_release,
|
|
|
|
};
|