aboutsummaryrefslogtreecommitdiffstats
path: root/fs/hfsplus
diff options
context:
space:
mode:
authorDavid Woodhouse <dwmw2@infradead.org>2007-07-23 05:20:10 -0400
committerDavid Woodhouse <dwmw2@infradead.org>2007-07-23 05:20:10 -0400
commit39fe5434cb9de5da40510028b17b96bc4eb312b3 (patch)
tree7a02a317b9ad57da51ca99887c119e779ccf3f13 /fs/hfsplus
parent0fc72b81d3111d114ab378935b1cf07680ca1289 (diff)
parentf695baf2df9e0413d3521661070103711545207a (diff)
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
Diffstat (limited to 'fs/hfsplus')
-rw-r--r--fs/hfsplus/btree.c4
-rw-r--r--fs/hfsplus/dir.c2
-rw-r--r--fs/hfsplus/hfsplus_fs.h4
-rw-r--r--fs/hfsplus/inode.c5
-rw-r--r--fs/hfsplus/ioctl.c2
-rw-r--r--fs/hfsplus/super.c6
-rw-r--r--fs/hfsplus/unicode.c230
7 files changed, 203 insertions, 50 deletions
diff --git a/fs/hfsplus/btree.c b/fs/hfsplus/btree.c
index 90ebab753d30..050d29c0a5b5 100644
--- a/fs/hfsplus/btree.c
+++ b/fs/hfsplus/btree.c
@@ -62,8 +62,10 @@ struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id)
62 if ((HFSPLUS_SB(sb).flags & HFSPLUS_SB_HFSX) && 62 if ((HFSPLUS_SB(sb).flags & HFSPLUS_SB_HFSX) &&
63 (head->key_type == HFSPLUS_KEY_BINARY)) 63 (head->key_type == HFSPLUS_KEY_BINARY))
64 tree->keycmp = hfsplus_cat_bin_cmp_key; 64 tree->keycmp = hfsplus_cat_bin_cmp_key;
65 else 65 else {
66 tree->keycmp = hfsplus_cat_case_cmp_key; 66 tree->keycmp = hfsplus_cat_case_cmp_key;
67 HFSPLUS_SB(sb).flags |= HFSPLUS_SB_CASEFOLD;
68 }
67 } else { 69 } else {
68 printk(KERN_ERR "hfs: unknown B*Tree requested\n"); 70 printk(KERN_ERR "hfs: unknown B*Tree requested\n");
69 goto fail_page; 71 goto fail_page;
diff --git a/fs/hfsplus/dir.c b/fs/hfsplus/dir.c
index 80b5682a2273..1955ee61251c 100644
--- a/fs/hfsplus/dir.c
+++ b/fs/hfsplus/dir.c
@@ -36,6 +36,8 @@ static struct dentry *hfsplus_lookup(struct inode *dir, struct dentry *dentry,
36 u16 type; 36 u16 type;
37 37
38 sb = dir->i_sb; 38 sb = dir->i_sb;
39
40 dentry->d_op = &hfsplus_dentry_operations;
39 dentry->d_fsdata = NULL; 41 dentry->d_fsdata = NULL;
40 hfs_find_init(HFSPLUS_SB(sb).cat_tree, &fd); 42 hfs_find_init(HFSPLUS_SB(sb).cat_tree, &fd);
41 hfsplus_cat_build_key(sb, fd.search_key, dir->i_ino, &dentry->d_name); 43 hfsplus_cat_build_key(sb, fd.search_key, dir->i_ino, &dentry->d_name);
diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h
index 3915635b4470..d9f5eda6d039 100644
--- a/fs/hfsplus/hfsplus_fs.h
+++ b/fs/hfsplus/hfsplus_fs.h
@@ -150,6 +150,7 @@ struct hfsplus_sb_info {
150#define HFSPLUS_SB_NODECOMPOSE 0x0002 150#define HFSPLUS_SB_NODECOMPOSE 0x0002
151#define HFSPLUS_SB_FORCE 0x0004 151#define HFSPLUS_SB_FORCE 0x0004
152#define HFSPLUS_SB_HFSX 0x0008 152#define HFSPLUS_SB_HFSX 0x0008
153#define HFSPLUS_SB_CASEFOLD 0x0010
153 154
154 155
155struct hfsplus_inode_info { 156struct hfsplus_inode_info {
@@ -321,6 +322,7 @@ void hfsplus_file_truncate(struct inode *);
321/* inode.c */ 322/* inode.c */
322extern const struct address_space_operations hfsplus_aops; 323extern const struct address_space_operations hfsplus_aops;
323extern const struct address_space_operations hfsplus_btree_aops; 324extern const struct address_space_operations hfsplus_btree_aops;
325extern struct dentry_operations hfsplus_dentry_operations;
324 326
325void hfsplus_inode_read_fork(struct inode *, struct hfsplus_fork_raw *); 327void hfsplus_inode_read_fork(struct inode *, struct hfsplus_fork_raw *);
326void hfsplus_inode_write_fork(struct inode *, struct hfsplus_fork_raw *); 328void hfsplus_inode_write_fork(struct inode *, struct hfsplus_fork_raw *);
@@ -353,6 +355,8 @@ int hfsplus_strcasecmp(const struct hfsplus_unistr *, const struct hfsplus_unist
353int hfsplus_strcmp(const struct hfsplus_unistr *, const struct hfsplus_unistr *); 355int hfsplus_strcmp(const struct hfsplus_unistr *, const struct hfsplus_unistr *);
354int hfsplus_uni2asc(struct super_block *, const struct hfsplus_unistr *, char *, int *); 356int hfsplus_uni2asc(struct super_block *, const struct hfsplus_unistr *, char *, int *);
355int hfsplus_asc2uni(struct super_block *, struct hfsplus_unistr *, const char *, int); 357int hfsplus_asc2uni(struct super_block *, struct hfsplus_unistr *, const char *, int);
358int hfsplus_hash_dentry(struct dentry *dentry, struct qstr *str);
359int hfsplus_compare_dentry(struct dentry *dentry, struct qstr *s1, struct qstr *s2);
356 360
357/* wrapper.c */ 361/* wrapper.c */
358int hfsplus_read_wrapper(struct super_block *); 362int hfsplus_read_wrapper(struct super_block *);
diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c
index 409ce5429c91..6f7c662174db 100644
--- a/fs/hfsplus/inode.c
+++ b/fs/hfsplus/inode.c
@@ -131,6 +131,11 @@ const struct address_space_operations hfsplus_aops = {
131 .writepages = hfsplus_writepages, 131 .writepages = hfsplus_writepages,
132}; 132};
133 133
134struct dentry_operations hfsplus_dentry_operations = {
135 .d_hash = hfsplus_hash_dentry,
136 .d_compare = hfsplus_compare_dentry,
137};
138
134static struct dentry *hfsplus_file_lookup(struct inode *dir, struct dentry *dentry, 139static struct dentry *hfsplus_file_lookup(struct inode *dir, struct dentry *dentry,
135 struct nameidata *nd) 140 struct nameidata *nd)
136{ 141{
diff --git a/fs/hfsplus/ioctl.c b/fs/hfsplus/ioctl.c
index 79fd10402ea3..b60c0affbec5 100644
--- a/fs/hfsplus/ioctl.c
+++ b/fs/hfsplus/ioctl.c
@@ -38,7 +38,7 @@ int hfsplus_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
38 if (IS_RDONLY(inode)) 38 if (IS_RDONLY(inode))
39 return -EROFS; 39 return -EROFS;
40 40
41 if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER)) 41 if (!is_owner_or_cap(inode))
42 return -EACCES; 42 return -EACCES;
43 43
44 if (get_user(flags, (int __user *)arg)) 44 if (get_user(flags, (int __user *)arg))
diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c
index ebd1b380cbbc..7b0f2e5a44e2 100644
--- a/fs/hfsplus/super.c
+++ b/fs/hfsplus/super.c
@@ -283,11 +283,10 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent)
283 struct nls_table *nls = NULL; 283 struct nls_table *nls = NULL;
284 int err = -EINVAL; 284 int err = -EINVAL;
285 285
286 sbi = kmalloc(sizeof(struct hfsplus_sb_info), GFP_KERNEL); 286 sbi = kzalloc(sizeof(*sbi), GFP_KERNEL);
287 if (!sbi) 287 if (!sbi)
288 return -ENOMEM; 288 return -ENOMEM;
289 289
290 memset(sbi, 0, sizeof(HFSPLUS_SB(sb)));
291 sb->s_fs_info = sbi; 290 sb->s_fs_info = sbi;
292 INIT_HLIST_HEAD(&sbi->rsrc_inodes); 291 INIT_HLIST_HEAD(&sbi->rsrc_inodes);
293 hfsplus_fill_defaults(sbi); 292 hfsplus_fill_defaults(sbi);
@@ -381,6 +380,7 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent)
381 iput(root); 380 iput(root);
382 goto cleanup; 381 goto cleanup;
383 } 382 }
383 sb->s_root->d_op = &hfsplus_dentry_operations;
384 384
385 str.len = sizeof(HFSP_HIDDENDIR_NAME) - 1; 385 str.len = sizeof(HFSP_HIDDENDIR_NAME) - 1;
386 str.name = HFSP_HIDDENDIR_NAME; 386 str.name = HFSP_HIDDENDIR_NAME;
@@ -479,7 +479,7 @@ static int __init init_hfsplus_fs(void)
479 479
480 hfsplus_inode_cachep = kmem_cache_create("hfsplus_icache", 480 hfsplus_inode_cachep = kmem_cache_create("hfsplus_icache",
481 HFSPLUS_INODE_SIZE, 0, SLAB_HWCACHE_ALIGN, 481 HFSPLUS_INODE_SIZE, 0, SLAB_HWCACHE_ALIGN,
482 hfsplus_init_once, NULL); 482 hfsplus_init_once);
483 if (!hfsplus_inode_cachep) 483 if (!hfsplus_inode_cachep)
484 return -ENOMEM; 484 return -ENOMEM;
485 err = register_filesystem(&hfsplus_fs_type); 485 err = register_filesystem(&hfsplus_fs_type);
diff --git a/fs/hfsplus/unicode.c b/fs/hfsplus/unicode.c
index 689c8bd721fb..9e10f9444b64 100644
--- a/fs/hfsplus/unicode.c
+++ b/fs/hfsplus/unicode.c
@@ -239,61 +239,201 @@ out:
239 return res; 239 return res;
240} 240}
241 241
242int hfsplus_asc2uni(struct super_block *sb, struct hfsplus_unistr *ustr, const char *astr, int len) 242/*
243 * Convert one or more ASCII characters into a single unicode character.
244 * Returns the number of ASCII characters corresponding to the unicode char.
245 */
246static inline int asc2unichar(struct super_block *sb, const char *astr, int len,
247 wchar_t *uc)
243{ 248{
244 struct nls_table *nls = HFSPLUS_SB(sb).nls; 249 int size = HFSPLUS_SB(sb).nls->char2uni(astr, len, uc);
245 int size, off, decompose; 250 if (size <= 0) {
251 *uc = '?';
252 size = 1;
253 }
254 switch (*uc) {
255 case 0x2400:
256 *uc = 0;
257 break;
258 case ':':
259 *uc = '/';
260 break;
261 }
262 return size;
263}
264
265/* Decomposes a single unicode character. */
266static inline u16 *decompose_unichar(wchar_t uc, int *size)
267{
268 int off;
269
270 off = hfsplus_decompose_table[(uc >> 12) & 0xf];
271 if (off == 0 || off == 0xffff)
272 return NULL;
273
274 off = hfsplus_decompose_table[off + ((uc >> 8) & 0xf)];
275 if (!off)
276 return NULL;
277
278 off = hfsplus_decompose_table[off + ((uc >> 4) & 0xf)];
279 if (!off)
280 return NULL;
281
282 off = hfsplus_decompose_table[off + (uc & 0xf)];
283 *size = off & 3;
284 if (*size == 0)
285 return NULL;
286 return hfsplus_decompose_table + (off / 4);
287}
288
289int hfsplus_asc2uni(struct super_block *sb, struct hfsplus_unistr *ustr,
290 const char *astr, int len)
291{
292 int size, dsize, decompose;
293 u16 *dstr, outlen = 0;
246 wchar_t c; 294 wchar_t c;
247 u16 outlen = 0;
248 295
249 decompose = !(HFSPLUS_SB(sb).flags & HFSPLUS_SB_NODECOMPOSE); 296 decompose = !(HFSPLUS_SB(sb).flags & HFSPLUS_SB_NODECOMPOSE);
250
251 while (outlen < HFSPLUS_MAX_STRLEN && len > 0) { 297 while (outlen < HFSPLUS_MAX_STRLEN && len > 0) {
252 size = nls->char2uni(astr, len, &c); 298 size = asc2unichar(sb, astr, len, &c);
253 if (size <= 0) { 299
254 c = '?'; 300 if (decompose && (dstr = decompose_unichar(c, &dsize))) {
255 size = 1; 301 if (outlen + dsize > HFSPLUS_MAX_STRLEN)
256 }
257 astr += size;
258 len -= size;
259 switch (c) {
260 case 0x2400:
261 c = 0;
262 break;
263 case ':':
264 c = '/';
265 break;
266 }
267 if (c >= 0xc0 && decompose) {
268 off = hfsplus_decompose_table[(c >> 12) & 0xf];
269 if (!off)
270 goto done;
271 if (off == 0xffff) {
272 goto done;
273 }
274 off = hfsplus_decompose_table[off + ((c >> 8) & 0xf)];
275 if (!off)
276 goto done;
277 off = hfsplus_decompose_table[off + ((c >> 4) & 0xf)];
278 if (!off)
279 goto done;
280 off = hfsplus_decompose_table[off + (c & 0xf)];
281 size = off & 3;
282 if (!size)
283 goto done;
284 off /= 4;
285 if (outlen + size > HFSPLUS_MAX_STRLEN)
286 break; 302 break;
287 do { 303 do {
288 ustr->unicode[outlen++] = cpu_to_be16(hfsplus_decompose_table[off++]); 304 ustr->unicode[outlen++] = cpu_to_be16(*dstr++);
289 } while (--size > 0); 305 } while (--dsize > 0);
290 continue; 306 } else
291 } 307 ustr->unicode[outlen++] = cpu_to_be16(c);
292 done: 308
293 ustr->unicode[outlen++] = cpu_to_be16(c); 309 astr += size;
310 len -= size;
294 } 311 }
295 ustr->length = cpu_to_be16(outlen); 312 ustr->length = cpu_to_be16(outlen);
296 if (len > 0) 313 if (len > 0)
297 return -ENAMETOOLONG; 314 return -ENAMETOOLONG;
298 return 0; 315 return 0;
299} 316}
317
318/*
319 * Hash a string to an integer as appropriate for the HFS+ filesystem.
320 * Composed unicode characters are decomposed and case-folding is performed
321 * if the appropriate bits are (un)set on the superblock.
322 */
323int hfsplus_hash_dentry(struct dentry *dentry, struct qstr *str)
324{
325 struct super_block *sb = dentry->d_sb;
326 const char *astr;
327 const u16 *dstr;
328 int casefold, decompose, size, dsize, len;
329 unsigned long hash;
330 wchar_t c;
331 u16 c2;
332
333 casefold = (HFSPLUS_SB(sb).flags & HFSPLUS_SB_CASEFOLD);
334 decompose = !(HFSPLUS_SB(sb).flags & HFSPLUS_SB_NODECOMPOSE);
335 hash = init_name_hash();
336 astr = str->name;
337 len = str->len;
338 while (len > 0) {
339 size = asc2unichar(sb, astr, len, &c);
340 astr += size;
341 len -= size;
342
343 if (decompose && (dstr = decompose_unichar(c, &dsize))) {
344 do {
345 c2 = *dstr++;
346 if (!casefold || (c2 = case_fold(c2)))
347 hash = partial_name_hash(c2, hash);
348 } while (--dsize > 0);
349 } else {
350 c2 = c;
351 if (!casefold || (c2 = case_fold(c2)))
352 hash = partial_name_hash(c2, hash);
353 }
354 }
355 str->hash = end_name_hash(hash);
356
357 return 0;
358}
359
360/*
361 * Compare strings with HFS+ filename ordering.
362 * Composed unicode characters are decomposed and case-folding is performed
363 * if the appropriate bits are (un)set on the superblock.
364 */
365int hfsplus_compare_dentry(struct dentry *dentry, struct qstr *s1, struct qstr *s2)
366{
367 struct super_block *sb = dentry->d_sb;
368 int casefold, decompose, size;
369 int dsize1, dsize2, len1, len2;
370 const u16 *dstr1, *dstr2;
371 const char *astr1, *astr2;
372 u16 c1, c2;
373 wchar_t c;
374
375 casefold = (HFSPLUS_SB(sb).flags & HFSPLUS_SB_CASEFOLD);
376 decompose = !(HFSPLUS_SB(sb).flags & HFSPLUS_SB_NODECOMPOSE);
377 astr1 = s1->name;
378 len1 = s1->len;
379 astr2 = s2->name;
380 len2 = s2->len;
381 dsize1 = dsize2 = 0;
382 dstr1 = dstr2 = NULL;
383
384 while (len1 > 0 && len2 > 0) {
385 if (!dsize1) {
386 size = asc2unichar(sb, astr1, len1, &c);
387 astr1 += size;
388 len1 -= size;
389
390 if (!decompose || !(dstr1 = decompose_unichar(c, &dsize1))) {
391 c1 = c;
392 dstr1 = &c1;
393 dsize1 = 1;
394 }
395 }
396
397 if (!dsize2) {
398 size = asc2unichar(sb, astr2, len2, &c);
399 astr2 += size;
400 len2 -= size;
401
402 if (!decompose || !(dstr2 = decompose_unichar(c, &dsize2))) {
403 c2 = c;
404 dstr2 = &c2;
405 dsize2 = 1;
406 }
407 }
408
409 c1 = *dstr1;
410 c2 = *dstr2;
411 if (casefold) {
412 if (!(c1 = case_fold(c1))) {
413 dstr1++;
414 dsize1--;
415 continue;
416 }
417 if (!(c2 = case_fold(c2))) {
418 dstr2++;
419 dsize2--;
420 continue;
421 }
422 }
423 if (c1 < c2)
424 return -1;
425 else if (c1 > c2)
426 return 1;
427
428 dstr1++;
429 dsize1--;
430 dstr2++;
431 dsize2--;
432 }
433
434 if (len1 < len2)
435 return -1;
436 if (len1 > len2)
437 return 1;
438 return 0;
439}