2005-04-17 02:20:36 +04: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 01:36:21 +04:00
# include <linux/nls.h>
2005-04-17 02:20:36 +04:00
# include "hfsplus_fs.h"
# include "hfsplus_raw.h"
2013-02-28 05:03:04 +04:00
# include "xattr.h"
2013-09-12 01:24:30 +04:00
# include "acl.h"
2005-04-17 02:20:36 +04: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 01:13:09 +04:00
unsigned int flags )
2005-04-17 02:20:36 +04: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 10:41:23 +04:00
2005-04-17 02:20:36 +04:00
dentry - > d_fsdata = NULL ;
2011-07-06 02:29:59 +04:00
err = hfs_find_init ( HFSPLUS_SB ( sb ) - > cat_tree , & fd ) ;
if ( err )
return ERR_PTR ( err ) ;
2014-12-19 03:17:12 +03: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 02:20:36 +04: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 19:08:38 +03: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 01:25:59 +03:00
HFSPLUS_I ( d_inode ( sb - > s_root ) ) - >
2010-12-16 19:08:38 +03:00
create_date ) & &
HFSPLUS_SB ( sb ) - > hidden_dir ) {
2005-04-17 02:20:36 +04:00
struct qstr str ;
char name [ 32 ] ;
if ( dentry - > d_fsdata ) {
2006-01-19 04:43:10 +03: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 19:08:38 +03:00
linkid =
be32_to_cpu ( entry . file . permissions . dev ) ;
2006-01-19 04:43:10 +03:00
str . len = sprintf ( name , " iNode%d " , linkid ) ;
str . name = name ;
2014-12-19 03:17:12 +03:00
err = hfsplus_cat_build_key ( sb , fd . search_key ,
2010-12-16 19:08:38 +03:00
HFSPLUS_SB ( sb ) - > hidden_dir - > i_ino ,
& str ) ;
2014-12-19 03:17:12 +03:00
if ( unlikely ( err < 0 ) )
goto fail ;
2006-01-19 04:43:10 +03:00
goto again ;
2005-04-17 02:20:36 +04:00
}
} else if ( ! dentry - > d_fsdata )
dentry - > d_fsdata = ( void * ) ( unsigned long ) cnid ;
} else {
2013-05-01 02:27:55 +04:00
pr_err ( " invalid catalog entry type in lookup \n " ) ;
2005-04-17 02:20:36 +04:00
err = - EIO ;
goto fail ;
}
hfs_find_exit ( & fd ) ;
2008-02-07 11:15:40 +03:00
inode = hfsplus_iget ( dir - > i_sb , cnid ) ;
if ( IS_ERR ( inode ) )
return ERR_CAST ( inode ) ;
2005-04-17 02:20:36 +04:00
if ( S_ISREG ( inode - > i_mode ) )
2010-10-14 17:54:28 +04:00
HFSPLUS_I ( inode ) - > linkid = linkid ;
2005-04-17 02:20:36 +04:00
out :
d_add ( dentry , inode ) ;
return NULL ;
fail :
hfs_find_exit ( & fd ) ;
return ERR_PTR ( err ) ;
}
2013-05-22 22:59:39 +04:00
static int hfsplus_readdir ( struct file * file , struct dir_context * ctx )
2005-04-17 02:20:36 +04:00
{
2013-05-22 22:59:39 +04:00
struct inode * inode = file_inode ( file ) ;
2005-04-17 02:20:36 +04: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 01:36:21 +04:00
char * strbuf ;
2005-04-17 02:20:36 +04:00
hfsplus_cat_entry entry ;
struct hfs_find_data fd ;
struct hfsplus_readdir_data * rd ;
u16 type ;
2013-05-22 22:59:39 +04:00
if ( file - > f_pos > = inode - > i_size )
2005-04-17 02:20:36 +04:00
return 0 ;
2011-07-06 02:29:59 +04: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 01:36:21 +04:00
strbuf = kmalloc ( NLS_MAX_CHARSET_SIZE * HFSPLUS_MAX_STRLEN + 1 , GFP_KERNEL ) ;
if ( ! strbuf ) {
err = - ENOMEM ;
goto out ;
}
2014-12-19 03:17:12 +03:00
hfsplus_cat_build_key_with_cnid ( sb , fd . search_key , inode - > i_ino ) ;
2013-02-28 05:03:04 +04:00
err = hfs_brec_find ( & fd , hfs_find_rec_by_key ) ;
2005-04-17 02:20:36 +04:00
if ( err )
goto out ;
2013-05-22 22:59:39 +04:00
if ( ctx - > pos = = 0 ) {
2005-04-17 02:20:36 +04:00
/* This is completely artificial... */
2013-05-22 22:59:39 +04:00
if ( ! dir_emit_dot ( file , ctx ) )
2005-04-17 02:20:36 +04:00
goto out ;
2013-05-22 22:59:39 +04:00
ctx - > pos = 1 ;
}
if ( ctx - > pos = = 1 ) {
2012-05-04 23:09:39 +04:00
if ( fd . entrylength > sizeof ( entry ) | | fd . entrylength < 0 ) {
err = - EIO ;
goto out ;
}
2010-12-16 19:08:38 +03:00
hfs_bnode_read ( fd . bnode , & entry , fd . entryoffset ,
fd . entrylength ) ;
2005-04-17 02:20:36 +04:00
if ( be16_to_cpu ( entry . type ) ! = HFSPLUS_FOLDER_THREAD ) {
2013-05-01 02:27:55 +04:00
pr_err ( " bad catalog folder thread \n " ) ;
2005-04-17 02:20:36 +04:00
err = - EIO ;
goto out ;
}
if ( fd . entrylength < HFSPLUS_MIN_THREAD_SZ ) {
2013-05-01 02:27:55 +04:00
pr_err ( " truncated catalog thread \n " ) ;
2005-04-17 02:20:36 +04:00
err = - EIO ;
goto out ;
}
2013-05-22 22:59:39 +04:00
if ( ! dir_emit ( ctx , " .. " , 2 ,
2005-04-17 02:20:36 +04:00
be32_to_cpu ( entry . thread . parentID ) , DT_DIR ) )
goto out ;
2013-05-22 22:59:39 +04:00
ctx - > pos = 2 ;
2005-04-17 02:20:36 +04:00
}
2013-05-22 22:59:39 +04:00
if ( ctx - > pos > = inode - > i_size )
goto out ;
err = hfs_brec_goto ( & fd , ctx - > pos - 1 ) ;
if ( err )
goto out ;
2005-04-17 02:20:36 +04:00
for ( ; ; ) {
if ( be32_to_cpu ( fd . key - > cat . parent ) ! = inode - > i_ino ) {
2013-05-01 02:27:55 +04:00
pr_err ( " walked past end of dir \n " ) ;
2005-04-17 02:20:36 +04:00
err = - EIO ;
goto out ;
}
2012-05-04 23:09:39 +04:00
if ( fd . entrylength > sizeof ( entry ) | | fd . entrylength < 0 ) {
err = - EIO ;
goto out ;
}
2010-12-16 19:08:38 +03:00
hfs_bnode_read ( fd . bnode , & entry , fd . entryoffset ,
fd . entrylength ) ;
2005-04-17 02:20:36 +04: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 01:36:21 +04:00
len = NLS_MAX_CHARSET_SIZE * HFSPLUS_MAX_STRLEN ;
2005-04-17 02:20:36 +04:00
err = hfsplus_uni2asc ( sb , & fd . key - > cat . name , strbuf , & len ) ;
if ( err )
goto out ;
if ( type = = HFSPLUS_FOLDER ) {
2010-12-16 19:08:38 +03:00
if ( fd . entrylength <
sizeof ( struct hfsplus_cat_folder ) ) {
2013-05-01 02:27:55 +04:00
pr_err ( " small dir entry \n " ) ;
2005-04-17 02:20:36 +04:00
err = - EIO ;
goto out ;
}
2010-10-01 07:42:59 +04:00
if ( HFSPLUS_SB ( sb ) - > hidden_dir & &
HFSPLUS_SB ( sb ) - > hidden_dir - > i_ino = =
be32_to_cpu ( entry . folder . id ) )
2005-04-17 02:20:36 +04:00
goto next ;
2013-05-22 22:59:39 +04:00
if ( ! dir_emit ( ctx , strbuf , len ,
2005-04-17 02:20:36 +04:00
be32_to_cpu ( entry . folder . id ) , DT_DIR ) )
break ;
} else if ( type = = HFSPLUS_FILE ) {
2014-06-07 01:36:24 +04:00
u16 mode ;
unsigned type = DT_UNKNOWN ;
2005-04-17 02:20:36 +04:00
if ( fd . entrylength < sizeof ( struct hfsplus_cat_file ) ) {
2013-05-01 02:27:55 +04:00
pr_err ( " small file entry \n " ) ;
2005-04-17 02:20:36 +04:00
err = - EIO ;
goto out ;
}
2014-06-07 01:36:24 +04: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-22 22:59:39 +04:00
if ( ! dir_emit ( ctx , strbuf , len ,
2014-06-07 01:36:24 +04:00
be32_to_cpu ( entry . file . id ) , type ) )
2005-04-17 02:20:36 +04:00
break ;
} else {
2013-05-01 02:27:55 +04:00
pr_err ( " bad catalog entry type \n " ) ;
2005-04-17 02:20:36 +04:00
err = - EIO ;
goto out ;
}
2010-12-16 19:08:40 +03:00
next :
2013-05-22 22:59:39 +04:00
ctx - > pos + + ;
if ( ctx - > pos > = inode - > i_size )
2005-04-17 02:20:36 +04:00
goto out ;
err = hfs_brec_goto ( & fd , 1 ) ;
if ( err )
goto out ;
}
2013-05-22 22:59:39 +04:00
rd = file - > private_data ;
2005-04-17 02:20:36 +04:00
if ( ! rd ) {
rd = kmalloc ( sizeof ( struct hfsplus_readdir_data ) , GFP_KERNEL ) ;
if ( ! rd ) {
err = - ENOMEM ;
goto out ;
}
2013-05-22 22:59:39 +04:00
file - > private_data = rd ;
rd - > file = file ;
2016-05-13 03:02:09 +03:00
spin_lock ( & HFSPLUS_I ( inode ) - > open_dir_lock ) ;
2010-10-01 07:43:31 +04:00
list_add ( & rd - > list , & HFSPLUS_I ( inode ) - > open_dir_list ) ;
2016-05-13 03:02:09 +03:00
spin_unlock ( & HFSPLUS_I ( inode ) - > open_dir_lock ) ;
2005-04-17 02:20:36 +04:00
}
2016-05-13 03:02:09 +03:00
/*
* Can be done after the list insertion ; exclusion with
* hfsplus_delete_cat ( ) is provided by directory lock .
*/
2005-04-17 02:20:36 +04: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 01:36:21 +04:00
kfree ( strbuf ) ;
2005-04-17 02:20:36 +04: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 03:02:09 +03:00
spin_lock ( & HFSPLUS_I ( inode ) - > open_dir_lock ) ;
2005-04-17 02:20:36 +04:00
list_del ( & rd - > list ) ;
2016-05-13 03:02:09 +03:00
spin_unlock ( & HFSPLUS_I ( inode ) - > open_dir_lock ) ;
2005-04-17 02:20:36 +04:00
kfree ( rd ) ;
}
return 0 ;
}
static int hfsplus_link ( struct dentry * src_dentry , struct inode * dst_dir ,
struct dentry * dst_dentry )
{
2010-10-01 07:42:59 +04:00
struct hfsplus_sb_info * sbi = HFSPLUS_SB ( dst_dir - > i_sb ) ;
2015-03-18 01:25:59 +03:00
struct inode * inode = d_inode ( src_dentry ) ;
struct inode * src_dir = d_inode ( src_dentry - > d_parent ) ;
2005-04-17 02:20:36 +04:00
struct qstr str ;
char name [ 32 ] ;
u32 cnid , id ;
int res ;
if ( HFSPLUS_IS_RSRC ( inode ) )
return - EPERM ;
2010-10-14 17:54:28 +04:00
if ( ! S_ISREG ( inode - > i_mode ) )
return - EPERM ;
2005-04-17 02:20:36 +04:00
2010-10-01 07:45:08 +04:00
mutex_lock ( & sbi - > vh_mutex ) ;
2005-04-17 02:20:36 +04: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 07:42:59 +04:00
sbi - > hidden_dir , & str ) ;
2005-04-17 02:20:36 +04:00
if ( ! res )
break ;
if ( res ! = - EEXIST )
2010-10-01 07:45:08 +04:00
goto out ;
2005-04-17 02:20:36 +04:00
}
2010-10-14 17:54:28 +04:00
HFSPLUS_I ( inode ) - > linkid = id ;
2010-10-01 07:42:59 +04:00
cnid = sbi - > next_cnid + + ;
2005-04-17 02:20:36 +04:00
src_dentry - > d_fsdata = ( void * ) ( unsigned long ) cnid ;
2010-12-16 19:08:38 +03:00
res = hfsplus_create_cat ( cnid , src_dir ,
& src_dentry - > d_name , inode ) ;
2005-04-17 02:20:36 +04:00
if ( res )
/* panic? */
2010-10-01 07:45:08 +04:00
goto out ;
2010-10-01 07:42:59 +04:00
sbi - > file_count + + ;
2005-04-17 02:20:36 +04:00
}
2010-10-01 07:42:59 +04:00
cnid = sbi - > next_cnid + + ;
2005-04-17 02:20:36 +04:00
res = hfsplus_create_cat ( cnid , dst_dir , & dst_dentry - > d_name , inode ) ;
if ( res )
2010-10-01 07:45:08 +04:00
goto out ;
2005-04-17 02:20:36 +04:00
2006-10-01 10:29:04 +04:00
inc_nlink ( inode ) ;
2005-04-17 02:20:36 +04:00
hfsplus_instantiate ( dst_dentry , inode , cnid ) ;
2010-10-23 19:11:40 +04:00
ihold ( inode ) ;
2016-09-14 17:48:05 +03:00
inode - > i_ctime = current_time ( inode ) ;
2005-04-17 02:20:36 +04:00
mark_inode_dirty ( inode ) ;
2010-10-01 07:42:59 +04:00
sbi - > file_count + + ;
2012-07-12 18:26:31 +04:00
hfsplus_mark_mdb_dirty ( dst_dir - > i_sb ) ;
2010-10-01 07:45:08 +04:00
out :
mutex_unlock ( & sbi - > vh_mutex ) ;
return res ;
2005-04-17 02:20:36 +04:00
}
static int hfsplus_unlink ( struct inode * dir , struct dentry * dentry )
{
2010-10-01 07:42:59 +04:00
struct hfsplus_sb_info * sbi = HFSPLUS_SB ( dir - > i_sb ) ;
2015-03-18 01:25:59 +03:00
struct inode * inode = d_inode ( dentry ) ;
2005-04-17 02:20:36 +04:00
struct qstr str ;
char name [ 32 ] ;
u32 cnid ;
int res ;
if ( HFSPLUS_IS_RSRC ( inode ) )
return - EPERM ;
2010-10-01 07:45:08 +04:00
mutex_lock ( & sbi - > vh_mutex ) ;
2005-04-17 02:20:36 +04:00
cnid = ( u32 ) ( unsigned long ) dentry - > d_fsdata ;
if ( inode - > i_ino = = cnid & &
2010-10-01 07:43:31 +04:00
atomic_read ( & HFSPLUS_I ( inode ) - > opencnt ) ) {
2005-04-17 02:20:36 +04: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 07:42:59 +04:00
sbi - > hidden_dir , & str ) ;
2010-10-27 15:45:50 +04:00
if ( ! res ) {
2005-04-17 02:20:36 +04:00
inode - > i_flags | = S_DEAD ;
2010-10-27 15:45:50 +04:00
drop_nlink ( inode ) ;
}
2010-10-01 07:45:08 +04:00
goto out ;
2005-04-17 02:20:36 +04:00
}
res = hfsplus_delete_cat ( cnid , dir , & dentry - > d_name ) ;
if ( res )
2010-10-01 07:45:08 +04:00
goto out ;
2005-04-17 02:20:36 +04:00
2006-01-19 04:43:10 +03:00
if ( inode - > i_nlink > 0 )
2006-10-01 10:29:03 +04:00
drop_nlink ( inode ) ;
2008-04-09 19:44:07 +04:00
if ( inode - > i_ino = = cnid )
clear_nlink ( inode ) ;
if ( ! inode - > i_nlink ) {
if ( inode - > i_ino ! = cnid ) {
2010-10-01 07:42:59 +04:00
sbi - > file_count - - ;
2010-10-01 07:43:31 +04:00
if ( ! atomic_read ( & HFSPLUS_I ( inode ) - > opencnt ) ) {
2008-04-09 19:44:07 +04:00
res = hfsplus_delete_cat ( inode - > i_ino ,
2010-10-01 07:42:59 +04:00
sbi - > hidden_dir ,
2008-04-09 19:44:07 +04:00
NULL ) ;
if ( ! res )
hfsplus_delete_inode ( inode ) ;
} else
inode - > i_flags | = S_DEAD ;
2005-04-17 02:20:36 +04:00
} else
2008-04-09 19:44:07 +04:00
hfsplus_delete_inode ( inode ) ;
2006-01-19 04:43:10 +03:00
} else
2010-10-01 07:42:59 +04:00
sbi - > file_count - - ;
2016-09-14 17:48:05 +03:00
inode - > i_ctime = current_time ( inode ) ;
2005-04-17 02:20:36 +04:00
mark_inode_dirty ( inode ) ;
2010-10-01 07:45:08 +04:00
out :
mutex_unlock ( & sbi - > vh_mutex ) ;
2005-04-17 02:20:36 +04:00
return res ;
}
static int hfsplus_rmdir ( struct inode * dir , struct dentry * dentry )
{
2010-10-01 07:45:08 +04:00
struct hfsplus_sb_info * sbi = HFSPLUS_SB ( dir - > i_sb ) ;
2015-03-18 01:25:59 +03:00
struct inode * inode = d_inode ( dentry ) ;
2005-04-17 02:20:36 +04:00
int res ;
if ( inode - > i_size ! = 2 )
return - ENOTEMPTY ;
2010-10-01 07:45:08 +04:00
mutex_lock ( & sbi - > vh_mutex ) ;
2005-04-17 02:20:36 +04:00
res = hfsplus_delete_cat ( inode - > i_ino , dir , & dentry - > d_name ) ;
if ( res )
2010-10-01 07:45:08 +04:00
goto out ;
2006-10-01 10:29:06 +04:00
clear_nlink ( inode ) ;
2016-09-14 17:48:05 +03:00
inode - > i_ctime = current_time ( inode ) ;
2005-04-17 02:20:36 +04:00
hfsplus_delete_inode ( inode ) ;
mark_inode_dirty ( inode ) ;
2010-10-01 07:45:08 +04:00
out :
mutex_unlock ( & sbi - > vh_mutex ) ;
return res ;
2005-04-17 02:20:36 +04:00
}
static int hfsplus_symlink ( struct inode * dir , struct dentry * dentry ,
const char * symname )
{
2010-10-01 07:45:08 +04:00
struct hfsplus_sb_info * sbi = HFSPLUS_SB ( dir - > i_sb ) ;
2005-04-17 02:20:36 +04:00
struct inode * inode ;
2015-04-16 22:47:12 +03:00
int res = - ENOMEM ;
2005-04-17 02:20:36 +04:00
2010-10-01 07:45:08 +04:00
mutex_lock ( & sbi - > vh_mutex ) ;
2010-10-01 07:43:54 +04:00
inode = hfsplus_new_inode ( dir - > i_sb , S_IFLNK | S_IRWXUGO ) ;
2005-04-17 02:20:36 +04:00
if ( ! inode )
2010-10-01 07:45:08 +04:00
goto out ;
2005-04-17 02:20:36 +04:00
res = page_symlink ( inode , symname , strlen ( symname ) + 1 ) ;
2010-10-01 07:43:54 +04:00
if ( res )
goto out_err ;
2005-04-17 02:20:36 +04:00
res = hfsplus_create_cat ( inode - > i_ino , dir , & dentry - > d_name , inode ) ;
2010-10-01 07:43:54 +04:00
if ( res )
goto out_err ;
2005-04-17 02:20:36 +04:00
2013-02-28 05:03:04 +04: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 07:43:54 +04:00
hfsplus_instantiate ( dentry , inode , inode - > i_ino ) ;
mark_inode_dirty ( inode ) ;
2010-10-01 07:45:08 +04:00
goto out ;
2005-04-17 02:20:36 +04:00
2010-10-01 07:43:54 +04:00
out_err :
2011-10-28 16:13:28 +04:00
clear_nlink ( inode ) ;
2010-10-01 07:43:54 +04:00
hfsplus_delete_inode ( inode ) ;
iput ( inode ) ;
2010-10-01 07:45:08 +04:00
out :
mutex_unlock ( & sbi - > vh_mutex ) ;
2005-04-17 02:20:36 +04:00
return res ;
}
static int hfsplus_mknod ( struct inode * dir , struct dentry * dentry ,
2011-07-26 09:52:52 +04:00
umode_t mode , dev_t rdev )
2005-04-17 02:20:36 +04:00
{
2010-10-01 07:45:08 +04:00
struct hfsplus_sb_info * sbi = HFSPLUS_SB ( dir - > i_sb ) ;
2005-04-17 02:20:36 +04:00
struct inode * inode ;
2015-04-16 22:47:12 +03:00
int res = - ENOMEM ;
2005-04-17 02:20:36 +04:00
2010-10-01 07:45:08 +04:00
mutex_lock ( & sbi - > vh_mutex ) ;
2010-10-01 07:43:50 +04:00
inode = hfsplus_new_inode ( dir - > i_sb , mode ) ;
2005-04-17 02:20:36 +04:00
if ( ! inode )
2010-10-01 07:45:08 +04:00
goto out ;
2005-04-17 02:20:36 +04:00
2010-10-14 17:54:39 +04:00
if ( S_ISBLK ( mode ) | | S_ISCHR ( mode ) | | S_ISFIFO ( mode ) | | S_ISSOCK ( mode ) )
init_special_inode ( inode , mode , rdev ) ;
2005-04-17 02:20:36 +04:00
res = hfsplus_create_cat ( inode - > i_ino , dir , & dentry - > d_name , inode ) ;
2013-02-28 05:03:04 +04: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 02:20:36 +04:00
}
2010-10-01 07:43:50 +04:00
2005-04-17 02:20:36 +04:00
hfsplus_instantiate ( dentry , inode , inode - > i_ino ) ;
mark_inode_dirty ( inode ) ;
2013-02-28 05:03:04 +04:00
goto out ;
failed_mknod :
clear_nlink ( inode ) ;
hfsplus_delete_inode ( inode ) ;
iput ( inode ) ;
2010-10-01 07:45:08 +04:00
out :
mutex_unlock ( & sbi - > vh_mutex ) ;
return res ;
2005-04-17 02:20:36 +04:00
}
2011-07-26 09:42:34 +04:00
static int hfsplus_create ( struct inode * dir , struct dentry * dentry , umode_t mode ,
2012-06-11 02:05:36 +04:00
bool excl )
2010-10-01 07:43:50 +04:00
{
return hfsplus_mknod ( dir , dentry , mode , 0 ) ;
}
2011-07-26 09:41:39 +04:00
static int hfsplus_mkdir ( struct inode * dir , struct dentry * dentry , umode_t mode )
2010-10-01 07:43:50 +04:00
{
return hfsplus_mknod ( dir , dentry , mode | S_IFDIR , 0 ) ;
}
2005-04-17 02:20:36 +04: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 12:03:57 +03:00
struct inode * new_dir , struct dentry * new_dentry ,
unsigned int flags )
2005-04-17 02:20:36 +04: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 12:03:57 +03:00
if ( flags & ~ RENAME_NOREPLACE )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
/* Unlink destination if it already exists */
2015-03-18 01:25:59 +03: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 15:02:35 +03:00
if ( d_is_dir ( new_dentry ) )
2010-10-01 11:12:08 +04:00
res = hfsplus_rmdir ( new_dir , new_dentry ) ;
2011-05-28 00:42:06 +04:00
else
2010-10-01 11:12:08 +04:00
res = hfsplus_unlink ( new_dir , new_dentry ) ;
2005-04-17 02:20:36 +04: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 11:55:39 +03:00
const struct inode_operations hfsplus_dir_inode_operations = {
2013-02-28 05:03:04 +04: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 01:24:30 +04:00
# ifdef CONFIG_HFSPLUS_FS_POSIX_ACL
. get_acl = hfsplus_get_posix_acl ,
2013-12-20 17:16:46 +04:00
. set_acl = hfsplus_set_posix_acl ,
2013-09-12 01:24:30 +04:00
# endif
2005-04-17 02:20:36 +04:00
} ;
2006-03-28 13:56:42 +04:00
const struct file_operations hfsplus_dir_operations = {
2010-11-23 16:38:10 +03:00
. fsync = hfsplus_file_fsync ,
2005-04-17 02:20:36 +04:00
. read = generic_read_dir ,
2016-05-13 03:02:09 +03:00
. iterate_shared = hfsplus_readdir ,
2010-04-27 18:24:20 +04:00
. unlocked_ioctl = hfsplus_ioctl ,
2005-04-17 02:20:36 +04:00
. llseek = generic_file_llseek ,
. release = hfsplus_dir_release ,
} ;