diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /fs/hfs |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'fs/hfs')
-rw-r--r-- | fs/hfs/Makefile | 10 | ||||
-rw-r--r-- | fs/hfs/attr.c | 121 | ||||
-rw-r--r-- | fs/hfs/bfind.c | 210 | ||||
-rw-r--r-- | fs/hfs/bitmap.c | 243 | ||||
-rw-r--r-- | fs/hfs/bnode.c | 498 | ||||
-rw-r--r-- | fs/hfs/brec.c | 496 | ||||
-rw-r--r-- | fs/hfs/btree.c | 327 | ||||
-rw-r--r-- | fs/hfs/btree.h | 168 | ||||
-rw-r--r-- | fs/hfs/catalog.c | 347 | ||||
-rw-r--r-- | fs/hfs/dir.c | 330 | ||||
-rw-r--r-- | fs/hfs/extent.c | 527 | ||||
-rw-r--r-- | fs/hfs/hfs.h | 287 | ||||
-rw-r--r-- | fs/hfs/hfs_fs.h | 286 | ||||
-rw-r--r-- | fs/hfs/inode.c | 636 | ||||
-rw-r--r-- | fs/hfs/mdb.c | 343 | ||||
-rw-r--r-- | fs/hfs/part_tbl.c | 117 | ||||
-rw-r--r-- | fs/hfs/string.c | 115 | ||||
-rw-r--r-- | fs/hfs/super.c | 395 | ||||
-rw-r--r-- | fs/hfs/sysdep.c | 40 | ||||
-rw-r--r-- | fs/hfs/trans.c | 72 |
20 files changed, 5568 insertions, 0 deletions
diff --git a/fs/hfs/Makefile b/fs/hfs/Makefile new file mode 100644 index 000000000000..c41f5a85f42d --- /dev/null +++ b/fs/hfs/Makefile | |||
@@ -0,0 +1,10 @@ | |||
1 | # | ||
2 | # Makefile for the Linux hfs filesystem routines. | ||
3 | # | ||
4 | |||
5 | obj-$(CONFIG_HFS_FS) += hfs.o | ||
6 | |||
7 | hfs-objs := bitmap.o bfind.o bnode.o brec.o btree.o \ | ||
8 | catalog.o dir.o extent.o inode.o attr.o mdb.o \ | ||
9 | part_tbl.o string.o super.o sysdep.o trans.o | ||
10 | |||
diff --git a/fs/hfs/attr.c b/fs/hfs/attr.c new file mode 100644 index 000000000000..e057ec542a6a --- /dev/null +++ b/fs/hfs/attr.c | |||
@@ -0,0 +1,121 @@ | |||
1 | /* | ||
2 | * linux/fs/hfs/attr.c | ||
3 | * | ||
4 | * (C) 2003 Ardis Technologies <roman@ardistech.com> | ||
5 | * | ||
6 | * Export hfs data via xattr | ||
7 | */ | ||
8 | |||
9 | |||
10 | #include <linux/fs.h> | ||
11 | #include <linux/xattr.h> | ||
12 | |||
13 | #include "hfs_fs.h" | ||
14 | #include "btree.h" | ||
15 | |||
16 | int hfs_setxattr(struct dentry *dentry, const char *name, | ||
17 | const void *value, size_t size, int flags) | ||
18 | { | ||
19 | struct inode *inode = dentry->d_inode; | ||
20 | struct hfs_find_data fd; | ||
21 | hfs_cat_rec rec; | ||
22 | struct hfs_cat_file *file; | ||
23 | int res; | ||
24 | |||
25 | if (!S_ISREG(inode->i_mode) || HFS_IS_RSRC(inode)) | ||
26 | return -EOPNOTSUPP; | ||
27 | |||
28 | res = hfs_find_init(HFS_SB(inode->i_sb)->cat_tree, &fd); | ||
29 | if (res) | ||
30 | return res; | ||
31 | fd.search_key->cat = HFS_I(inode)->cat_key; | ||
32 | res = hfs_brec_find(&fd); | ||
33 | if (res) | ||
34 | goto out; | ||
35 | hfs_bnode_read(fd.bnode, &rec, fd.entryoffset, | ||
36 | sizeof(struct hfs_cat_file)); | ||
37 | file = &rec.file; | ||
38 | |||
39 | if (!strcmp(name, "hfs.type")) { | ||
40 | if (size == 4) | ||
41 | memcpy(&file->UsrWds.fdType, value, 4); | ||
42 | else | ||
43 | res = -ERANGE; | ||
44 | } else if (!strcmp(name, "hfs.creator")) { | ||
45 | if (size == 4) | ||
46 | memcpy(&file->UsrWds.fdCreator, value, 4); | ||
47 | else | ||
48 | res = -ERANGE; | ||
49 | } else | ||
50 | res = -EOPNOTSUPP; | ||
51 | if (!res) | ||
52 | hfs_bnode_write(fd.bnode, &rec, fd.entryoffset, | ||
53 | sizeof(struct hfs_cat_file)); | ||
54 | out: | ||
55 | hfs_find_exit(&fd); | ||
56 | return res; | ||
57 | } | ||
58 | |||
59 | ssize_t hfs_getxattr(struct dentry *dentry, const char *name, | ||
60 | void *value, size_t size) | ||
61 | { | ||
62 | struct inode *inode = dentry->d_inode; | ||
63 | struct hfs_find_data fd; | ||
64 | hfs_cat_rec rec; | ||
65 | struct hfs_cat_file *file; | ||
66 | ssize_t res = 0; | ||
67 | |||
68 | if (!S_ISREG(inode->i_mode) || HFS_IS_RSRC(inode)) | ||
69 | return -EOPNOTSUPP; | ||
70 | |||
71 | if (size) { | ||
72 | res = hfs_find_init(HFS_SB(inode->i_sb)->cat_tree, &fd); | ||
73 | if (res) | ||
74 | return res; | ||
75 | fd.search_key->cat = HFS_I(inode)->cat_key; | ||
76 | res = hfs_brec_find(&fd); | ||
77 | if (res) | ||
78 | goto out; | ||
79 | hfs_bnode_read(fd.bnode, &rec, fd.entryoffset, | ||
80 | sizeof(struct hfs_cat_file)); | ||
81 | } | ||
82 | file = &rec.file; | ||
83 | |||
84 | if (!strcmp(name, "hfs.type")) { | ||
85 | if (size >= 4) { | ||
86 | memcpy(value, &file->UsrWds.fdType, 4); | ||
87 | res = 4; | ||
88 | } else | ||
89 | res = size ? -ERANGE : 4; | ||
90 | } else if (!strcmp(name, "hfs.creator")) { | ||
91 | if (size >= 4) { | ||
92 | memcpy(value, &file->UsrWds.fdCreator, 4); | ||
93 | res = 4; | ||
94 | } else | ||
95 | res = size ? -ERANGE : 4; | ||
96 | } else | ||
97 | res = -ENODATA; | ||
98 | out: | ||
99 | if (size) | ||
100 | hfs_find_exit(&fd); | ||
101 | return res; | ||
102 | } | ||
103 | |||
104 | #define HFS_ATTRLIST_SIZE (sizeof("hfs.creator")+sizeof("hfs.type")) | ||
105 | |||
106 | ssize_t hfs_listxattr(struct dentry *dentry, char *buffer, size_t size) | ||
107 | { | ||
108 | struct inode *inode = dentry->d_inode; | ||
109 | |||
110 | if (!S_ISREG(inode->i_mode) || HFS_IS_RSRC(inode)) | ||
111 | return -EOPNOTSUPP; | ||
112 | |||
113 | if (!buffer || !size) | ||
114 | return HFS_ATTRLIST_SIZE; | ||
115 | if (size < HFS_ATTRLIST_SIZE) | ||
116 | return -ERANGE; | ||
117 | strcpy(buffer, "hfs.type"); | ||
118 | strcpy(buffer + sizeof("hfs.type"), "hfs.creator"); | ||
119 | |||
120 | return HFS_ATTRLIST_SIZE; | ||
121 | } | ||
diff --git a/fs/hfs/bfind.c b/fs/hfs/bfind.c new file mode 100644 index 000000000000..89450ae32228 --- /dev/null +++ b/fs/hfs/bfind.c | |||
@@ -0,0 +1,210 @@ | |||
1 | /* | ||
2 | * linux/fs/hfs/bfind.c | ||
3 | * | ||
4 | * Copyright (C) 2001 | ||
5 | * Brad Boyer (flar@allandria.com) | ||
6 | * (C) 2003 Ardis Technologies <roman@ardistech.com> | ||
7 | * | ||
8 | * Search routines for btrees | ||
9 | */ | ||
10 | |||
11 | #include <linux/slab.h> | ||
12 | #include "btree.h" | ||
13 | |||
14 | int hfs_find_init(struct hfs_btree *tree, struct hfs_find_data *fd) | ||
15 | { | ||
16 | void *ptr; | ||
17 | |||
18 | fd->tree = tree; | ||
19 | fd->bnode = NULL; | ||
20 | ptr = kmalloc(tree->max_key_len * 2 + 4, GFP_KERNEL); | ||
21 | if (!ptr) | ||
22 | return -ENOMEM; | ||
23 | fd->search_key = ptr; | ||
24 | fd->key = ptr + tree->max_key_len + 2; | ||
25 | dprint(DBG_BNODE_REFS, "find_init: %d (%p)\n", tree->cnid, __builtin_return_address(0)); | ||
26 | down(&tree->tree_lock); | ||
27 | return 0; | ||
28 | } | ||
29 | |||
30 | void hfs_find_exit(struct hfs_find_data *fd) | ||
31 | { | ||
32 | hfs_bnode_put(fd->bnode); | ||
33 | kfree(fd->search_key); | ||
34 | dprint(DBG_BNODE_REFS, "find_exit: %d (%p)\n", fd->tree->cnid, __builtin_return_address(0)); | ||
35 | up(&fd->tree->tree_lock); | ||
36 | fd->tree = NULL; | ||
37 | } | ||
38 | |||
39 | /* Find the record in bnode that best matches key (not greater than...)*/ | ||
40 | int __hfs_brec_find(struct hfs_bnode *bnode, struct hfs_find_data *fd) | ||
41 | { | ||
42 | int cmpval; | ||
43 | u16 off, len, keylen; | ||
44 | int rec; | ||
45 | int b, e; | ||
46 | int res; | ||
47 | |||
48 | b = 0; | ||
49 | e = bnode->num_recs - 1; | ||
50 | res = -ENOENT; | ||
51 | do { | ||
52 | rec = (e + b) / 2; | ||
53 | len = hfs_brec_lenoff(bnode, rec, &off); | ||
54 | keylen = hfs_brec_keylen(bnode, rec); | ||
55 | hfs_bnode_read(bnode, fd->key, off, keylen); | ||
56 | cmpval = bnode->tree->keycmp(fd->key, fd->search_key); | ||
57 | if (!cmpval) { | ||
58 | e = rec; | ||
59 | res = 0; | ||
60 | goto done; | ||
61 | } | ||
62 | if (cmpval < 0) | ||
63 | b = rec + 1; | ||
64 | else | ||
65 | e = rec - 1; | ||
66 | } while (b <= e); | ||
67 | //printk("%d: %d,%d,%d\n", bnode->this, b, e, rec); | ||
68 | if (rec != e && e >= 0) { | ||
69 | len = hfs_brec_lenoff(bnode, e, &off); | ||
70 | keylen = hfs_brec_keylen(bnode, e); | ||
71 | hfs_bnode_read(bnode, fd->key, off, keylen); | ||
72 | } | ||
73 | done: | ||
74 | fd->record = e; | ||
75 | fd->keyoffset = off; | ||
76 | fd->keylength = keylen; | ||
77 | fd->entryoffset = off + keylen; | ||
78 | fd->entrylength = len - keylen; | ||
79 | return res; | ||
80 | } | ||
81 | |||
82 | /* Traverse a B*Tree from the root to a leaf finding best fit to key */ | ||
83 | /* Return allocated copy of node found, set recnum to best record */ | ||
84 | int hfs_brec_find(struct hfs_find_data *fd) | ||
85 | { | ||
86 | struct hfs_btree *tree; | ||
87 | struct hfs_bnode *bnode; | ||
88 | u32 nidx, parent; | ||
89 | __be32 data; | ||
90 | int height, res; | ||
91 | |||
92 | tree = fd->tree; | ||
93 | if (fd->bnode) | ||
94 | hfs_bnode_put(fd->bnode); | ||
95 | fd->bnode = NULL; | ||
96 | nidx = tree->root; | ||
97 | if (!nidx) | ||
98 | return -ENOENT; | ||
99 | height = tree->depth; | ||
100 | res = 0; | ||
101 | parent = 0; | ||
102 | for (;;) { | ||
103 | bnode = hfs_bnode_find(tree, nidx); | ||
104 | if (IS_ERR(bnode)) { | ||
105 | res = PTR_ERR(bnode); | ||
106 | bnode = NULL; | ||
107 | break; | ||
108 | } | ||
109 | if (bnode->height != height) | ||
110 | goto invalid; | ||
111 | if (bnode->type != (--height ? HFS_NODE_INDEX : HFS_NODE_LEAF)) | ||
112 | goto invalid; | ||
113 | bnode->parent = parent; | ||
114 | |||
115 | res = __hfs_brec_find(bnode, fd); | ||
116 | if (!height) | ||
117 | break; | ||
118 | if (fd->record < 0) | ||
119 | goto release; | ||
120 | |||
121 | parent = nidx; | ||
122 | hfs_bnode_read(bnode, &data, fd->entryoffset, 4); | ||
123 | nidx = be32_to_cpu(data); | ||
124 | hfs_bnode_put(bnode); | ||
125 | } | ||
126 | fd->bnode = bnode; | ||
127 | return res; | ||
128 | |||
129 | invalid: | ||
130 | printk("HFS: inconsistency in B*Tree (%d,%d,%d,%u,%u)\n", | ||
131 | height, bnode->height, bnode->type, nidx, parent); | ||
132 | res = -EIO; | ||
133 | release: | ||
134 | hfs_bnode_put(bnode); | ||
135 | return res; | ||
136 | } | ||
137 | |||
138 | int hfs_brec_read(struct hfs_find_data *fd, void *rec, int rec_len) | ||
139 | { | ||
140 | int res; | ||
141 | |||
142 | res = hfs_brec_find(fd); | ||
143 | if (res) | ||
144 | return res; | ||
145 | if (fd->entrylength > rec_len) | ||
146 | return -EINVAL; | ||
147 | hfs_bnode_read(fd->bnode, rec, fd->entryoffset, fd->entrylength); | ||
148 | return 0; | ||
149 | } | ||
150 | |||
151 | int hfs_brec_goto(struct hfs_find_data *fd, int cnt) | ||
152 | { | ||
153 | struct hfs_btree *tree; | ||
154 | struct hfs_bnode *bnode; | ||
155 | int idx, res = 0; | ||
156 | u16 off, len, keylen; | ||
157 | |||
158 | bnode = fd->bnode; | ||
159 | tree = bnode->tree; | ||
160 | |||
161 | if (cnt < 0) { | ||
162 | cnt = -cnt; | ||
163 | while (cnt > fd->record) { | ||
164 | cnt -= fd->record + 1; | ||
165 | fd->record = bnode->num_recs - 1; | ||
166 | idx = bnode->prev; | ||
167 | if (!idx) { | ||
168 | res = -ENOENT; | ||
169 | goto out; | ||
170 | } | ||
171 | hfs_bnode_put(bnode); | ||
172 | bnode = hfs_bnode_find(tree, idx); | ||
173 | if (IS_ERR(bnode)) { | ||
174 | res = PTR_ERR(bnode); | ||
175 | bnode = NULL; | ||
176 | goto out; | ||
177 | } | ||
178 | } | ||
179 | fd->record -= cnt; | ||
180 | } else { | ||
181 | while (cnt >= bnode->num_recs - fd->record) { | ||
182 | cnt -= bnode->num_recs - fd->record; | ||
183 | fd->record = 0; | ||
184 | idx = bnode->next; | ||
185 | if (!idx) { | ||
186 | res = -ENOENT; | ||
187 | goto out; | ||
188 | } | ||
189 | hfs_bnode_put(bnode); | ||
190 | bnode = hfs_bnode_find(tree, idx); | ||
191 | if (IS_ERR(bnode)) { | ||
192 | res = PTR_ERR(bnode); | ||
193 | bnode = NULL; | ||
194 | goto out; | ||
195 | } | ||
196 | } | ||
197 | fd->record += cnt; | ||
198 | } | ||
199 | |||
200 | len = hfs_brec_lenoff(bnode, fd->record, &off); | ||
201 | keylen = hfs_brec_keylen(bnode, fd->record); | ||
202 | fd->keyoffset = off; | ||
203 | fd->keylength = keylen; | ||
204 | fd->entryoffset = off + keylen; | ||
205 | fd->entrylength = len - keylen; | ||
206 | hfs_bnode_read(bnode, fd->key, off, keylen); | ||
207 | out: | ||
208 | fd->bnode = bnode; | ||
209 | return res; | ||
210 | } | ||
diff --git a/fs/hfs/bitmap.c b/fs/hfs/bitmap.c new file mode 100644 index 000000000000..24e75798ddf0 --- /dev/null +++ b/fs/hfs/bitmap.c | |||
@@ -0,0 +1,243 @@ | |||
1 | /* | ||
2 | * linux/fs/hfs/bitmap.c | ||
3 | * | ||
4 | * Copyright (C) 1996-1997 Paul H. Hargrove | ||
5 | * (C) 2003 Ardis Technologies <roman@ardistech.com> | ||
6 | * This file may be distributed under the terms of the GNU General Public License. | ||
7 | * | ||
8 | * Based on GPLed code Copyright (C) 1995 Michael Dreher | ||
9 | * | ||
10 | * This file contains the code to modify the volume bitmap: | ||
11 | * search/set/clear bits. | ||
12 | */ | ||
13 | |||
14 | #include "hfs_fs.h" | ||
15 | |||
16 | /* | ||
17 | * hfs_find_zero_bit() | ||
18 | * | ||
19 | * Description: | ||
20 | * Given a block of memory, its length in bits, and a starting bit number, | ||
21 | * determine the number of the first zero bits (in left-to-right ordering) | ||
22 | * in that range. | ||
23 | * | ||
24 | * Returns >= 'size' if no zero bits are found in the range. | ||
25 | * | ||
26 | * Accesses memory in 32-bit aligned chunks of 32-bits and thus | ||
27 | * may read beyond the 'size'th bit. | ||
28 | */ | ||
29 | static u32 hfs_find_set_zero_bits(__be32 *bitmap, u32 size, u32 offset, u32 *max) | ||
30 | { | ||
31 | __be32 *curr, *end; | ||
32 | u32 mask, start, len, n; | ||
33 | __be32 val; | ||
34 | int i; | ||
35 | |||
36 | len = *max; | ||
37 | if (!len) | ||
38 | return size; | ||
39 | |||
40 | curr = bitmap + (offset / 32); | ||
41 | end = bitmap + ((size + 31) / 32); | ||
42 | |||
43 | /* scan the first partial u32 for zero bits */ | ||
44 | val = *curr; | ||
45 | if (~val) { | ||
46 | n = be32_to_cpu(val); | ||
47 | i = offset % 32; | ||
48 | mask = (1U << 31) >> i; | ||
49 | for (; i < 32; mask >>= 1, i++) { | ||
50 | if (!(n & mask)) | ||
51 | goto found; | ||
52 | } | ||
53 | } | ||
54 | |||
55 | /* scan complete u32s for the first zero bit */ | ||
56 | while (++curr < end) { | ||
57 | val = *curr; | ||
58 | if (~val) { | ||
59 | n = be32_to_cpu(val); | ||
60 | mask = 1 << 31; | ||
61 | for (i = 0; i < 32; mask >>= 1, i++) { | ||
62 | if (!(n & mask)) | ||
63 | goto found; | ||
64 | } | ||
65 | } | ||
66 | } | ||
67 | return size; | ||
68 | |||
69 | found: | ||
70 | start = (curr - bitmap) * 32 + i; | ||
71 | if (start >= size) | ||
72 | return start; | ||
73 | /* do any partial u32 at the start */ | ||
74 | len = min(size - start, len); | ||
75 | while (1) { | ||
76 | n |= mask; | ||
77 | if (++i >= 32) | ||
78 | break; | ||
79 | mask >>= 1; | ||
80 | if (!--len || n & mask) | ||
81 | goto done; | ||
82 | } | ||
83 | if (!--len) | ||
84 | goto done; | ||
85 | *curr++ = cpu_to_be32(n); | ||
86 | /* do full u32s */ | ||
87 | while (1) { | ||
88 | n = be32_to_cpu(*curr); | ||
89 | if (len < 32) | ||
90 | break; | ||
91 | if (n) { | ||
92 | len = 32; | ||
93 | break; | ||
94 | } | ||
95 | *curr++ = cpu_to_be32(0xffffffff); | ||
96 | len -= 32; | ||
97 | } | ||
98 | /* do any partial u32 at end */ | ||
99 | mask = 1U << 31; | ||
100 | for (i = 0; i < len; i++) { | ||
101 | if (n & mask) | ||
102 | break; | ||
103 | n |= mask; | ||
104 | mask >>= 1; | ||
105 | } | ||
106 | done: | ||
107 | *curr = cpu_to_be32(n); | ||
108 | *max = (curr - bitmap) * 32 + i - start; | ||
109 | return start; | ||
110 | } | ||
111 | |||
112 | /* | ||
113 | * hfs_vbm_search_free() | ||
114 | * | ||
115 | * Description: | ||
116 | * Search for 'num_bits' consecutive cleared bits in the bitmap blocks of | ||
117 | * the hfs MDB. 'mdb' had better be locked or the returned range | ||
118 | * may be no longer free, when this functions returns! | ||
119 | * XXX Currently the search starts from bit 0, but it should start with | ||
120 | * the bit number stored in 's_alloc_ptr' of the MDB. | ||
121 | * Input Variable(s): | ||
122 | * struct hfs_mdb *mdb: Pointer to the hfs MDB | ||
123 | * u16 *num_bits: Pointer to the number of cleared bits | ||
124 | * to search for | ||
125 | * Output Variable(s): | ||
126 | * u16 *num_bits: The number of consecutive clear bits of the | ||
127 | * returned range. If the bitmap is fragmented, this will be less than | ||
128 | * requested and it will be zero, when the disk is full. | ||
129 | * Returns: | ||
130 | * The number of the first bit of the range of cleared bits which has been | ||
131 | * found. When 'num_bits' is zero, this is invalid! | ||
132 | * Preconditions: | ||
133 | * 'mdb' points to a "valid" (struct hfs_mdb). | ||
134 | * 'num_bits' points to a variable of type (u16), which contains | ||
135 | * the number of cleared bits to find. | ||
136 | * Postconditions: | ||
137 | * 'num_bits' is set to the length of the found sequence. | ||
138 | */ | ||
139 | u32 hfs_vbm_search_free(struct super_block *sb, u32 goal, u32 *num_bits) | ||
140 | { | ||
141 | void *bitmap; | ||
142 | u32 pos; | ||
143 | |||
144 | /* make sure we have actual work to perform */ | ||
145 | if (!*num_bits) | ||
146 | return 0; | ||
147 | |||
148 | down(&HFS_SB(sb)->bitmap_lock); | ||
149 | bitmap = HFS_SB(sb)->bitmap; | ||
150 | |||
151 | pos = hfs_find_set_zero_bits(bitmap, HFS_SB(sb)->fs_ablocks, goal, num_bits); | ||
152 | if (pos >= HFS_SB(sb)->fs_ablocks) { | ||
153 | if (goal) | ||
154 | pos = hfs_find_set_zero_bits(bitmap, goal, 0, num_bits); | ||
155 | if (pos >= HFS_SB(sb)->fs_ablocks) { | ||
156 | *num_bits = pos = 0; | ||
157 | goto out; | ||
158 | } | ||
159 | } | ||
160 | |||
161 | dprint(DBG_BITMAP, "alloc_bits: %u,%u\n", pos, *num_bits); | ||
162 | HFS_SB(sb)->free_ablocks -= *num_bits; | ||
163 | hfs_bitmap_dirty(sb); | ||
164 | out: | ||
165 | up(&HFS_SB(sb)->bitmap_lock); | ||
166 | return pos; | ||
167 | } | ||
168 | |||
169 | |||
170 | /* | ||
171 | * hfs_clear_vbm_bits() | ||
172 | * | ||
173 | * Description: | ||
174 | * Clear the requested bits in the volume bitmap of the hfs filesystem | ||
175 | * Input Variable(s): | ||
176 | * struct hfs_mdb *mdb: Pointer to the hfs MDB | ||
177 | * u16 start: The offset of the first bit | ||
178 | * u16 count: The number of bits | ||
179 | * Output Variable(s): | ||
180 | * None | ||
181 | * Returns: | ||
182 | * 0: no error | ||
183 | * -1: One of the bits was already clear. This is a strange | ||
184 | * error and when it happens, the filesystem must be repaired! | ||
185 | * -2: One or more of the bits are out of range of the bitmap. | ||
186 | * Preconditions: | ||
187 | * 'mdb' points to a "valid" (struct hfs_mdb). | ||
188 | * Postconditions: | ||
189 | * Starting with bit number 'start', 'count' bits in the volume bitmap | ||
190 | * are cleared. The affected bitmap blocks are marked "dirty", the free | ||
191 | * block count of the MDB is updated and the MDB is marked dirty. | ||
192 | */ | ||
193 | int hfs_clear_vbm_bits(struct super_block *sb, u16 start, u16 count) | ||
194 | { | ||
195 | __be32 *curr; | ||
196 | u32 mask; | ||
197 | int i, len; | ||
198 | |||
199 | /* is there any actual work to be done? */ | ||
200 | if (!count) | ||
201 | return 0; | ||
202 | |||
203 | dprint(DBG_BITMAP, "clear_bits: %u,%u\n", start, count); | ||
204 | /* are all of the bits in range? */ | ||
205 | if ((start + count) > HFS_SB(sb)->fs_ablocks) | ||
206 | return -2; | ||
207 | |||
208 | down(&HFS_SB(sb)->bitmap_lock); | ||
209 | /* bitmap is always on a 32-bit boundary */ | ||
210 | curr = HFS_SB(sb)->bitmap + (start / 32); | ||
211 | len = count; | ||
212 | |||
213 | /* do any partial u32 at the start */ | ||
214 | i = start % 32; | ||
215 | if (i) { | ||
216 | int j = 32 - i; | ||
217 | mask = 0xffffffffU << j; | ||
218 | if (j > count) { | ||
219 | mask |= 0xffffffffU >> (i + count); | ||
220 | *curr &= cpu_to_be32(mask); | ||
221 | goto out; | ||
222 | } | ||
223 | *curr++ &= cpu_to_be32(mask); | ||
224 | count -= j; | ||
225 | } | ||
226 | |||
227 | /* do full u32s */ | ||
228 | while (count >= 32) { | ||
229 | *curr++ = 0; | ||
230 | count -= 32; | ||
231 | } | ||
232 | /* do any partial u32 at end */ | ||
233 | if (count) { | ||
234 | mask = 0xffffffffU >> count; | ||
235 | *curr &= cpu_to_be32(mask); | ||
236 | } | ||
237 | out: | ||
238 | HFS_SB(sb)->free_ablocks += len; | ||
239 | up(&HFS_SB(sb)->bitmap_lock); | ||
240 | hfs_bitmap_dirty(sb); | ||
241 | |||
242 | return 0; | ||
243 | } | ||
diff --git a/fs/hfs/bnode.c b/fs/hfs/bnode.c new file mode 100644 index 000000000000..6ad1211f84ed --- /dev/null +++ b/fs/hfs/bnode.c | |||
@@ -0,0 +1,498 @@ | |||
1 | /* | ||
2 | * linux/fs/hfs/bnode.c | ||
3 | * | ||
4 | * Copyright (C) 2001 | ||
5 | * Brad Boyer (flar@allandria.com) | ||
6 | * (C) 2003 Ardis Technologies <roman@ardistech.com> | ||
7 | * | ||
8 | * Handle basic btree node operations | ||
9 | */ | ||
10 | |||
11 | #include <linux/pagemap.h> | ||
12 | #include <linux/swap.h> | ||
13 | |||
14 | #include "btree.h" | ||
15 | |||
16 | #define REF_PAGES 0 | ||
17 | |||
18 | void hfs_bnode_read(struct hfs_bnode *node, void *buf, | ||
19 | int off, int len) | ||
20 | { | ||
21 | struct page *page; | ||
22 | |||
23 | off += node->page_offset; | ||
24 | page = node->page[0]; | ||
25 | |||
26 | memcpy(buf, kmap(page) + off, len); | ||
27 | kunmap(page); | ||
28 | } | ||
29 | |||
30 | u16 hfs_bnode_read_u16(struct hfs_bnode *node, int off) | ||
31 | { | ||
32 | __be16 data; | ||
33 | // optimize later... | ||
34 | hfs_bnode_read(node, &data, off, 2); | ||
35 | return be16_to_cpu(data); | ||
36 | } | ||
37 | |||
38 | u8 hfs_bnode_read_u8(struct hfs_bnode *node, int off) | ||
39 | { | ||
40 | u8 data; | ||
41 | // optimize later... | ||
42 | hfs_bnode_read(node, &data, off, 1); | ||
43 | return data; | ||
44 | } | ||
45 | |||
46 | void hfs_bnode_read_key(struct hfs_bnode *node, void *key, int off) | ||
47 | { | ||
48 | struct hfs_btree *tree; | ||
49 | int key_len; | ||
50 | |||
51 | tree = node->tree; | ||
52 | if (node->type == HFS_NODE_LEAF || | ||
53 | tree->attributes & HFS_TREE_VARIDXKEYS) | ||
54 | key_len = hfs_bnode_read_u8(node, off) + 1; | ||
55 | else | ||
56 | key_len = tree->max_key_len + 1; | ||
57 | |||
58 | hfs_bnode_read(node, key, off, key_len); | ||
59 | } | ||
60 | |||
61 | void hfs_bnode_write(struct hfs_bnode *node, void *buf, int off, int len) | ||
62 | { | ||
63 | struct page *page; | ||
64 | |||
65 | off += node->page_offset; | ||
66 | page = node->page[0]; | ||
67 | |||
68 | memcpy(kmap(page) + off, buf, len); | ||
69 | kunmap(page); | ||
70 | set_page_dirty(page); | ||
71 | } | ||
72 | |||
73 | void hfs_bnode_write_u16(struct hfs_bnode *node, int off, u16 data) | ||
74 | { | ||
75 | __be16 v = cpu_to_be16(data); | ||
76 | // optimize later... | ||
77 | hfs_bnode_write(node, &v, off, 2); | ||
78 | } | ||
79 | |||
80 | void hfs_bnode_write_u8(struct hfs_bnode *node, int off, u8 data) | ||
81 | { | ||
82 | // optimize later... | ||
83 | hfs_bnode_write(node, &data, off, 1); | ||
84 | } | ||
85 | |||
86 | void hfs_bnode_clear(struct hfs_bnode *node, int off, int len) | ||
87 | { | ||
88 | struct page *page; | ||
89 | |||
90 | off += node->page_offset; | ||
91 | page = node->page[0]; | ||
92 | |||
93 | memset(kmap(page) + off, 0, len); | ||
94 | kunmap(page); | ||
95 | set_page_dirty(page); | ||
96 | } | ||
97 | |||
98 | void hfs_bnode_copy(struct hfs_bnode *dst_node, int dst, | ||
99 | struct hfs_bnode *src_node, int src, int len) | ||
100 | { | ||
101 | struct hfs_btree *tree; | ||
102 | struct page *src_page, *dst_page; | ||
103 | |||
104 | dprint(DBG_BNODE_MOD, "copybytes: %u,%u,%u\n", dst, src, len); | ||
105 | if (!len) | ||
106 | return; | ||
107 | tree = src_node->tree; | ||
108 | src += src_node->page_offset; | ||
109 | dst += dst_node->page_offset; | ||
110 | src_page = src_node->page[0]; | ||
111 | dst_page = dst_node->page[0]; | ||
112 | |||
113 | memcpy(kmap(dst_page) + dst, kmap(src_page) + src, len); | ||
114 | kunmap(src_page); | ||
115 | kunmap(dst_page); | ||
116 | set_page_dirty(dst_page); | ||
117 | } | ||
118 | |||
119 | void hfs_bnode_move(struct hfs_bnode *node, int dst, int src, int len) | ||
120 | { | ||
121 | struct page *page; | ||
122 | void *ptr; | ||
123 | |||
124 | dprint(DBG_BNODE_MOD, "movebytes: %u,%u,%u\n", dst, src, len); | ||
125 | if (!len) | ||
126 | return; | ||
127 | src += node->page_offset; | ||
128 | dst += node->page_offset; | ||
129 | page = node->page[0]; | ||
130 | ptr = kmap(page); | ||
131 | memmove(ptr + dst, ptr + src, len); | ||
132 | kunmap(page); | ||
133 | set_page_dirty(page); | ||
134 | } | ||
135 | |||
136 | void hfs_bnode_dump(struct hfs_bnode *node) | ||
137 | { | ||
138 | struct hfs_bnode_desc desc; | ||
139 | __be32 cnid; | ||
140 | int i, off, key_off; | ||
141 | |||
142 | dprint(DBG_BNODE_MOD, "bnode: %d\n", node->this); | ||
143 | hfs_bnode_read(node, &desc, 0, sizeof(desc)); | ||
144 | dprint(DBG_BNODE_MOD, "%d, %d, %d, %d, %d\n", | ||
145 | be32_to_cpu(desc.next), be32_to_cpu(desc.prev), | ||
146 | desc.type, desc.height, be16_to_cpu(desc.num_recs)); | ||
147 | |||
148 | off = node->tree->node_size - 2; | ||
149 | for (i = be16_to_cpu(desc.num_recs); i >= 0; off -= 2, i--) { | ||
150 | key_off = hfs_bnode_read_u16(node, off); | ||
151 | dprint(DBG_BNODE_MOD, " %d", key_off); | ||
152 | if (i && node->type == HFS_NODE_INDEX) { | ||
153 | int tmp; | ||
154 | |||
155 | if (node->tree->attributes & HFS_TREE_VARIDXKEYS) | ||
156 | tmp = (hfs_bnode_read_u8(node, key_off) | 1) + 1; | ||
157 | else | ||
158 | tmp = node->tree->max_key_len + 1; | ||
159 | dprint(DBG_BNODE_MOD, " (%d,%d", tmp, hfs_bnode_read_u8(node, key_off)); | ||
160 | hfs_bnode_read(node, &cnid, key_off + tmp, 4); | ||
161 | dprint(DBG_BNODE_MOD, ",%d)", be32_to_cpu(cnid)); | ||
162 | } else if (i && node->type == HFS_NODE_LEAF) { | ||
163 | int tmp; | ||
164 | |||
165 | tmp = hfs_bnode_read_u8(node, key_off); | ||
166 | dprint(DBG_BNODE_MOD, " (%d)", tmp); | ||
167 | } | ||
168 | } | ||
169 | dprint(DBG_BNODE_MOD, "\n"); | ||
170 | } | ||
171 | |||
172 | void hfs_bnode_unlink(struct hfs_bnode *node) | ||
173 | { | ||
174 | struct hfs_btree *tree; | ||
175 | struct hfs_bnode *tmp; | ||
176 | __be32 cnid; | ||
177 | |||
178 | tree = node->tree; | ||
179 | if (node->prev) { | ||
180 | tmp = hfs_bnode_find(tree, node->prev); | ||
181 | if (IS_ERR(tmp)) | ||
182 | return; | ||
183 | tmp->next = node->next; | ||
184 | cnid = cpu_to_be32(tmp->next); | ||
185 | hfs_bnode_write(tmp, &cnid, offsetof(struct hfs_bnode_desc, next), 4); | ||
186 | hfs_bnode_put(tmp); | ||
187 | } else if (node->type == HFS_NODE_LEAF) | ||
188 | tree->leaf_head = node->next; | ||
189 | |||
190 | if (node->next) { | ||
191 | tmp = hfs_bnode_find(tree, node->next); | ||
192 | if (IS_ERR(tmp)) | ||
193 | return; | ||
194 | tmp->prev = node->prev; | ||
195 | cnid = cpu_to_be32(tmp->prev); | ||
196 | hfs_bnode_write(tmp, &cnid, offsetof(struct hfs_bnode_desc, prev), 4); | ||
197 | hfs_bnode_put(tmp); | ||
198 | } else if (node->type == HFS_NODE_LEAF) | ||
199 | tree->leaf_tail = node->prev; | ||
200 | |||
201 | // move down? | ||
202 | if (!node->prev && !node->next) { | ||
203 | printk("hfs_btree_del_level\n"); | ||
204 | } | ||
205 | if (!node->parent) { | ||
206 | tree->root = 0; | ||
207 | tree->depth = 0; | ||
208 | } | ||
209 | set_bit(HFS_BNODE_DELETED, &node->flags); | ||
210 | } | ||
211 | |||
212 | static inline int hfs_bnode_hash(u32 num) | ||
213 | { | ||
214 | num = (num >> 16) + num; | ||
215 | num += num >> 8; | ||
216 | return num & (NODE_HASH_SIZE - 1); | ||
217 | } | ||
218 | |||
219 | struct hfs_bnode *hfs_bnode_findhash(struct hfs_btree *tree, u32 cnid) | ||
220 | { | ||
221 | struct hfs_bnode *node; | ||
222 | |||
223 | if (cnid >= tree->node_count) { | ||
224 | printk("HFS: request for non-existent node %d in B*Tree\n", cnid); | ||
225 | return NULL; | ||
226 | } | ||
227 | |||
228 | for (node = tree->node_hash[hfs_bnode_hash(cnid)]; | ||
229 | node; node = node->next_hash) { | ||
230 | if (node->this == cnid) { | ||
231 | return node; | ||
232 | } | ||
233 | } | ||
234 | return NULL; | ||
235 | } | ||
236 | |||
237 | static struct hfs_bnode *__hfs_bnode_create(struct hfs_btree *tree, u32 cnid) | ||
238 | { | ||
239 | struct super_block *sb; | ||
240 | struct hfs_bnode *node, *node2; | ||
241 | struct address_space *mapping; | ||
242 | struct page *page; | ||
243 | int size, block, i, hash; | ||
244 | loff_t off; | ||
245 | |||
246 | if (cnid >= tree->node_count) { | ||
247 | printk("HFS: request for non-existent node %d in B*Tree\n", cnid); | ||
248 | return NULL; | ||
249 | } | ||
250 | |||
251 | sb = tree->inode->i_sb; | ||
252 | size = sizeof(struct hfs_bnode) + tree->pages_per_bnode * | ||
253 | sizeof(struct page *); | ||
254 | node = kmalloc(size, GFP_KERNEL); | ||
255 | if (!node) | ||
256 | return NULL; | ||
257 | memset(node, 0, size); | ||
258 | node->tree = tree; | ||
259 | node->this = cnid; | ||
260 | set_bit(HFS_BNODE_NEW, &node->flags); | ||
261 | atomic_set(&node->refcnt, 1); | ||
262 | dprint(DBG_BNODE_REFS, "new_node(%d:%d): 1\n", | ||
263 | node->tree->cnid, node->this); | ||
264 | init_waitqueue_head(&node->lock_wq); | ||
265 | spin_lock(&tree->hash_lock); | ||
266 | node2 = hfs_bnode_findhash(tree, cnid); | ||
267 | if (!node2) { | ||
268 | hash = hfs_bnode_hash(cnid); | ||
269 | node->next_hash = tree->node_hash[hash]; | ||
270 | tree->node_hash[hash] = node; | ||
271 | tree->node_hash_cnt++; | ||
272 | } else { | ||
273 | spin_unlock(&tree->hash_lock); | ||
274 | kfree(node); | ||
275 | wait_event(node2->lock_wq, !test_bit(HFS_BNODE_NEW, &node2->flags)); | ||
276 | return node2; | ||
277 | } | ||
278 | spin_unlock(&tree->hash_lock); | ||
279 | |||
280 | mapping = tree->inode->i_mapping; | ||
281 | off = (loff_t)cnid * tree->node_size; | ||
282 | block = off >> PAGE_CACHE_SHIFT; | ||
283 | node->page_offset = off & ~PAGE_CACHE_MASK; | ||
284 | for (i = 0; i < tree->pages_per_bnode; i++) { | ||
285 | page = read_cache_page(mapping, block++, (filler_t *)mapping->a_ops->readpage, NULL); | ||
286 | if (IS_ERR(page)) | ||
287 | goto fail; | ||
288 | if (PageError(page)) { | ||
289 | page_cache_release(page); | ||
290 | goto fail; | ||
291 | } | ||
292 | #if !REF_PAGES | ||
293 | page_cache_release(page); | ||
294 | #endif | ||
295 | node->page[i] = page; | ||
296 | } | ||
297 | |||
298 | return node; | ||
299 | fail: | ||
300 | set_bit(HFS_BNODE_ERROR, &node->flags); | ||
301 | return node; | ||
302 | } | ||
303 | |||
304 | void hfs_bnode_unhash(struct hfs_bnode *node) | ||
305 | { | ||
306 | struct hfs_bnode **p; | ||
307 | |||
308 | dprint(DBG_BNODE_REFS, "remove_node(%d:%d): %d\n", | ||
309 | node->tree->cnid, node->this, atomic_read(&node->refcnt)); | ||
310 | for (p = &node->tree->node_hash[hfs_bnode_hash(node->this)]; | ||
311 | *p && *p != node; p = &(*p)->next_hash) | ||
312 | ; | ||
313 | if (!*p) | ||
314 | BUG(); | ||
315 | *p = node->next_hash; | ||
316 | node->tree->node_hash_cnt--; | ||
317 | } | ||
318 | |||
319 | /* Load a particular node out of a tree */ | ||
320 | struct hfs_bnode *hfs_bnode_find(struct hfs_btree *tree, u32 num) | ||
321 | { | ||
322 | struct hfs_bnode *node; | ||
323 | struct hfs_bnode_desc *desc; | ||
324 | int i, rec_off, off, next_off; | ||
325 | int entry_size, key_size; | ||
326 | |||
327 | spin_lock(&tree->hash_lock); | ||
328 | node = hfs_bnode_findhash(tree, num); | ||
329 | if (node) { | ||
330 | hfs_bnode_get(node); | ||
331 | spin_unlock(&tree->hash_lock); | ||
332 | wait_event(node->lock_wq, !test_bit(HFS_BNODE_NEW, &node->flags)); | ||
333 | if (test_bit(HFS_BNODE_ERROR, &node->flags)) | ||
334 | goto node_error; | ||
335 | return node; | ||
336 | } | ||
337 | spin_unlock(&tree->hash_lock); | ||
338 | node = __hfs_bnode_create(tree, num); | ||
339 | if (!node) | ||
340 | return ERR_PTR(-ENOMEM); | ||
341 | if (test_bit(HFS_BNODE_ERROR, &node->flags)) | ||
342 | goto node_error; | ||
343 | if (!test_bit(HFS_BNODE_NEW, &node->flags)) | ||
344 | return node; | ||
345 | |||
346 | desc = (struct hfs_bnode_desc *)(kmap(node->page[0]) + node->page_offset); | ||
347 | node->prev = be32_to_cpu(desc->prev); | ||
348 | node->next = be32_to_cpu(desc->next); | ||
349 | node->num_recs = be16_to_cpu(desc->num_recs); | ||
350 | node->type = desc->type; | ||
351 | node->height = desc->height; | ||
352 | kunmap(node->page[0]); | ||
353 | |||
354 | switch (node->type) { | ||
355 | case HFS_NODE_HEADER: | ||
356 | case HFS_NODE_MAP: | ||
357 | if (node->height != 0) | ||
358 | goto node_error; | ||
359 | break; | ||
360 | case HFS_NODE_LEAF: | ||
361 | if (node->height != 1) | ||
362 | goto node_error; | ||
363 | break; | ||
364 | case HFS_NODE_INDEX: | ||
365 | if (node->height <= 1 || node->height > tree->depth) | ||
366 | goto node_error; | ||
367 | break; | ||
368 | default: | ||
369 | goto node_error; | ||
370 | } | ||
371 | |||
372 | rec_off = tree->node_size - 2; | ||
373 | off = hfs_bnode_read_u16(node, rec_off); | ||
374 | if (off != sizeof(struct hfs_bnode_desc)) | ||
375 | goto node_error; | ||
376 | for (i = 1; i <= node->num_recs; off = next_off, i++) { | ||
377 | rec_off -= 2; | ||
378 | next_off = hfs_bnode_read_u16(node, rec_off); | ||
379 | if (next_off <= off || | ||
380 | next_off > tree->node_size || | ||
381 | next_off & 1) | ||
382 | goto node_error; | ||
383 | entry_size = next_off - off; | ||
384 | if (node->type != HFS_NODE_INDEX && | ||
385 | node->type != HFS_NODE_LEAF) | ||
386 | continue; | ||
387 | key_size = hfs_bnode_read_u8(node, off) + 1; | ||
388 | if (key_size >= entry_size /*|| key_size & 1*/) | ||
389 | goto node_error; | ||
390 | } | ||
391 | clear_bit(HFS_BNODE_NEW, &node->flags); | ||
392 | wake_up(&node->lock_wq); | ||
393 | return node; | ||
394 | |||
395 | node_error: | ||
396 | set_bit(HFS_BNODE_ERROR, &node->flags); | ||
397 | clear_bit(HFS_BNODE_NEW, &node->flags); | ||
398 | wake_up(&node->lock_wq); | ||
399 | hfs_bnode_put(node); | ||
400 | return ERR_PTR(-EIO); | ||
401 | } | ||
402 | |||
403 | void hfs_bnode_free(struct hfs_bnode *node) | ||
404 | { | ||
405 | //int i; | ||
406 | |||
407 | //for (i = 0; i < node->tree->pages_per_bnode; i++) | ||
408 | // if (node->page[i]) | ||
409 | // page_cache_release(node->page[i]); | ||
410 | kfree(node); | ||
411 | } | ||
412 | |||
413 | struct hfs_bnode *hfs_bnode_create(struct hfs_btree *tree, u32 num) | ||
414 | { | ||
415 | struct hfs_bnode *node; | ||
416 | struct page **pagep; | ||
417 | int i; | ||
418 | |||
419 | spin_lock(&tree->hash_lock); | ||
420 | node = hfs_bnode_findhash(tree, num); | ||
421 | spin_unlock(&tree->hash_lock); | ||
422 | if (node) | ||
423 | BUG(); | ||
424 | node = __hfs_bnode_create(tree, num); | ||
425 | if (!node) | ||
426 | return ERR_PTR(-ENOMEM); | ||
427 | if (test_bit(HFS_BNODE_ERROR, &node->flags)) { | ||
428 | hfs_bnode_put(node); | ||
429 | return ERR_PTR(-EIO); | ||
430 | } | ||
431 | |||
432 | pagep = node->page; | ||
433 | memset(kmap(*pagep) + node->page_offset, 0, | ||
434 | min((int)PAGE_CACHE_SIZE, (int)tree->node_size)); | ||
435 | set_page_dirty(*pagep); | ||
436 | kunmap(*pagep); | ||
437 | for (i = 1; i < tree->pages_per_bnode; i++) { | ||
438 | memset(kmap(*++pagep), 0, PAGE_CACHE_SIZE); | ||
439 | set_page_dirty(*pagep); | ||
440 | kunmap(*pagep); | ||
441 | } | ||
442 | clear_bit(HFS_BNODE_NEW, &node->flags); | ||
443 | wake_up(&node->lock_wq); | ||
444 | |||
445 | return node; | ||
446 | } | ||
447 | |||
448 | void hfs_bnode_get(struct hfs_bnode *node) | ||
449 | { | ||
450 | if (node) { | ||
451 | atomic_inc(&node->refcnt); | ||
452 | #if REF_PAGES | ||
453 | { | ||
454 | int i; | ||
455 | for (i = 0; i < node->tree->pages_per_bnode; i++) | ||
456 | get_page(node->page[i]); | ||
457 | } | ||
458 | #endif | ||
459 | dprint(DBG_BNODE_REFS, "get_node(%d:%d): %d\n", | ||
460 | node->tree->cnid, node->this, atomic_read(&node->refcnt)); | ||
461 | } | ||
462 | } | ||
463 | |||
464 | /* Dispose of resources used by a node */ | ||
465 | void hfs_bnode_put(struct hfs_bnode *node) | ||
466 | { | ||
467 | if (node) { | ||
468 | struct hfs_btree *tree = node->tree; | ||
469 | int i; | ||
470 | |||
471 | dprint(DBG_BNODE_REFS, "put_node(%d:%d): %d\n", | ||
472 | node->tree->cnid, node->this, atomic_read(&node->refcnt)); | ||
473 | if (!atomic_read(&node->refcnt)) | ||
474 | BUG(); | ||
475 | if (!atomic_dec_and_lock(&node->refcnt, &tree->hash_lock)) { | ||
476 | #if REF_PAGES | ||
477 | for (i = 0; i < tree->pages_per_bnode; i++) | ||
478 | put_page(node->page[i]); | ||
479 | #endif | ||
480 | return; | ||
481 | } | ||
482 | for (i = 0; i < tree->pages_per_bnode; i++) { | ||
483 | mark_page_accessed(node->page[i]); | ||
484 | #if REF_PAGES | ||
485 | put_page(node->page[i]); | ||
486 | #endif | ||
487 | } | ||
488 | |||
489 | if (test_bit(HFS_BNODE_DELETED, &node->flags)) { | ||
490 | hfs_bnode_unhash(node); | ||
491 | spin_unlock(&tree->hash_lock); | ||
492 | hfs_bmap_free(node); | ||
493 | hfs_bnode_free(node); | ||
494 | return; | ||
495 | } | ||
496 | spin_unlock(&tree->hash_lock); | ||
497 | } | ||
498 | } | ||
diff --git a/fs/hfs/brec.c b/fs/hfs/brec.c new file mode 100644 index 000000000000..7d8fff2c25fc --- /dev/null +++ b/fs/hfs/brec.c | |||
@@ -0,0 +1,496 @@ | |||
1 | /* | ||
2 | * linux/fs/hfs/brec.c | ||
3 | * | ||
4 | * Copyright (C) 2001 | ||
5 | * Brad Boyer (flar@allandria.com) | ||
6 | * (C) 2003 Ardis Technologies <roman@ardistech.com> | ||
7 | * | ||
8 | * Handle individual btree records | ||
9 | */ | ||
10 | |||
11 | #include "btree.h" | ||
12 | |||
13 | static struct hfs_bnode *hfs_bnode_split(struct hfs_find_data *fd); | ||
14 | static int hfs_brec_update_parent(struct hfs_find_data *fd); | ||
15 | static int hfs_btree_inc_height(struct hfs_btree *tree); | ||
16 | |||
17 | /* Get the length and offset of the given record in the given node */ | ||
18 | u16 hfs_brec_lenoff(struct hfs_bnode *node, u16 rec, u16 *off) | ||
19 | { | ||
20 | __be16 retval[2]; | ||
21 | u16 dataoff; | ||
22 | |||
23 | dataoff = node->tree->node_size - (rec + 2) * 2; | ||
24 | hfs_bnode_read(node, retval, dataoff, 4); | ||
25 | *off = be16_to_cpu(retval[1]); | ||
26 | return be16_to_cpu(retval[0]) - *off; | ||
27 | } | ||
28 | |||
29 | /* Get the length of the key from a keyed record */ | ||
30 | u16 hfs_brec_keylen(struct hfs_bnode *node, u16 rec) | ||
31 | { | ||
32 | u16 retval, recoff; | ||
33 | |||
34 | if (node->type != HFS_NODE_INDEX && node->type != HFS_NODE_LEAF) | ||
35 | return 0; | ||
36 | |||
37 | if ((node->type == HFS_NODE_INDEX) && | ||
38 | !(node->tree->attributes & HFS_TREE_VARIDXKEYS)) { | ||
39 | if (node->tree->attributes & HFS_TREE_BIGKEYS) | ||
40 | retval = node->tree->max_key_len + 2; | ||
41 | else | ||
42 | retval = node->tree->max_key_len + 1; | ||
43 | } else { | ||
44 | recoff = hfs_bnode_read_u16(node, node->tree->node_size - (rec + 1) * 2); | ||
45 | if (!recoff) | ||
46 | return 0; | ||
47 | if (node->tree->attributes & HFS_TREE_BIGKEYS) | ||
48 | retval = hfs_bnode_read_u16(node, recoff) + 2; | ||
49 | else | ||
50 | retval = (hfs_bnode_read_u8(node, recoff) | 1) + 1; | ||
51 | } | ||
52 | return retval; | ||
53 | } | ||
54 | |||
55 | int hfs_brec_insert(struct hfs_find_data *fd, void *entry, int entry_len) | ||
56 | { | ||
57 | struct hfs_btree *tree; | ||
58 | struct hfs_bnode *node, *new_node; | ||
59 | int size, key_len, rec; | ||
60 | int data_off, end_off; | ||
61 | int idx_rec_off, data_rec_off, end_rec_off; | ||
62 | __be32 cnid; | ||
63 | |||
64 | tree = fd->tree; | ||
65 | if (!fd->bnode) { | ||
66 | if (!tree->root) | ||
67 | hfs_btree_inc_height(tree); | ||
68 | fd->bnode = hfs_bnode_find(tree, tree->leaf_head); | ||
69 | if (IS_ERR(fd->bnode)) | ||
70 | return PTR_ERR(fd->bnode); | ||
71 | fd->record = -1; | ||
72 | } | ||
73 | new_node = NULL; | ||
74 | key_len = (fd->search_key->key_len | 1) + 1; | ||
75 | again: | ||
76 | /* new record idx and complete record size */ | ||
77 | rec = fd->record + 1; | ||
78 | size = key_len + entry_len; | ||
79 | |||
80 | node = fd->bnode; | ||
81 | hfs_bnode_dump(node); | ||
82 | /* get last offset */ | ||
83 | end_rec_off = tree->node_size - (node->num_recs + 1) * 2; | ||
84 | end_off = hfs_bnode_read_u16(node, end_rec_off); | ||
85 | end_rec_off -= 2; | ||
86 | dprint(DBG_BNODE_MOD, "insert_rec: %d, %d, %d, %d\n", rec, size, end_off, end_rec_off); | ||
87 | if (size > end_rec_off - end_off) { | ||
88 | if (new_node) | ||
89 | panic("not enough room!\n"); | ||
90 | new_node = hfs_bnode_split(fd); | ||
91 | if (IS_ERR(new_node)) | ||
92 | return PTR_ERR(new_node); | ||
93 | goto again; | ||
94 | } | ||
95 | if (node->type == HFS_NODE_LEAF) { | ||
96 | tree->leaf_count++; | ||
97 | mark_inode_dirty(tree->inode); | ||
98 | } | ||
99 | node->num_recs++; | ||
100 | /* write new last offset */ | ||
101 | hfs_bnode_write_u16(node, offsetof(struct hfs_bnode_desc, num_recs), node->num_recs); | ||
102 | hfs_bnode_write_u16(node, end_rec_off, end_off + size); | ||
103 | data_off = end_off; | ||
104 | data_rec_off = end_rec_off + 2; | ||
105 | idx_rec_off = tree->node_size - (rec + 1) * 2; | ||
106 | if (idx_rec_off == data_rec_off) | ||
107 | goto skip; | ||
108 | /* move all following entries */ | ||
109 | do { | ||
110 | data_off = hfs_bnode_read_u16(node, data_rec_off + 2); | ||
111 | hfs_bnode_write_u16(node, data_rec_off, data_off + size); | ||
112 | data_rec_off += 2; | ||
113 | } while (data_rec_off < idx_rec_off); | ||
114 | |||
115 | /* move data away */ | ||
116 | hfs_bnode_move(node, data_off + size, data_off, | ||
117 | end_off - data_off); | ||
118 | |||
119 | skip: | ||
120 | hfs_bnode_write(node, fd->search_key, data_off, key_len); | ||
121 | hfs_bnode_write(node, entry, data_off + key_len, entry_len); | ||
122 | hfs_bnode_dump(node); | ||
123 | |||
124 | if (new_node) { | ||
125 | /* update parent key if we inserted a key | ||
126 | * at the start of the first node | ||
127 | */ | ||
128 | if (!rec && new_node != node) | ||
129 | hfs_brec_update_parent(fd); | ||
130 | |||
131 | hfs_bnode_put(fd->bnode); | ||
132 | if (!new_node->parent) { | ||
133 | hfs_btree_inc_height(tree); | ||
134 | new_node->parent = tree->root; | ||
135 | } | ||
136 | fd->bnode = hfs_bnode_find(tree, new_node->parent); | ||
137 | |||
138 | /* create index data entry */ | ||
139 | cnid = cpu_to_be32(new_node->this); | ||
140 | entry = &cnid; | ||
141 | entry_len = sizeof(cnid); | ||
142 | |||
143 | /* get index key */ | ||
144 | hfs_bnode_read_key(new_node, fd->search_key, 14); | ||
145 | __hfs_brec_find(fd->bnode, fd); | ||
146 | |||
147 | hfs_bnode_put(new_node); | ||
148 | new_node = NULL; | ||
149 | |||
150 | if (tree->attributes & HFS_TREE_VARIDXKEYS) | ||
151 | key_len = fd->search_key->key_len + 1; | ||
152 | else { | ||
153 | fd->search_key->key_len = tree->max_key_len; | ||
154 | key_len = tree->max_key_len + 1; | ||
155 | } | ||
156 | goto again; | ||
157 | } | ||
158 | |||
159 | if (!rec) | ||
160 | hfs_brec_update_parent(fd); | ||
161 | |||
162 | return 0; | ||
163 | } | ||
164 | |||
165 | int hfs_brec_remove(struct hfs_find_data *fd) | ||
166 | { | ||
167 | struct hfs_btree *tree; | ||
168 | struct hfs_bnode *node, *parent; | ||
169 | int end_off, rec_off, data_off, size; | ||
170 | |||
171 | tree = fd->tree; | ||
172 | node = fd->bnode; | ||
173 | again: | ||
174 | rec_off = tree->node_size - (fd->record + 2) * 2; | ||
175 | end_off = tree->node_size - (node->num_recs + 1) * 2; | ||
176 | |||
177 | if (node->type == HFS_NODE_LEAF) { | ||
178 | tree->leaf_count--; | ||
179 | mark_inode_dirty(tree->inode); | ||
180 | } | ||
181 | hfs_bnode_dump(node); | ||
182 | dprint(DBG_BNODE_MOD, "remove_rec: %d, %d\n", fd->record, fd->keylength + fd->entrylength); | ||
183 | if (!--node->num_recs) { | ||
184 | hfs_bnode_unlink(node); | ||
185 | if (!node->parent) | ||
186 | return 0; | ||
187 | parent = hfs_bnode_find(tree, node->parent); | ||
188 | if (IS_ERR(parent)) | ||
189 | return PTR_ERR(parent); | ||
190 | hfs_bnode_put(node); | ||
191 | node = fd->bnode = parent; | ||
192 | |||
193 | __hfs_brec_find(node, fd); | ||
194 | goto again; | ||
195 | } | ||
196 | hfs_bnode_write_u16(node, offsetof(struct hfs_bnode_desc, num_recs), node->num_recs); | ||
197 | |||
198 | if (rec_off == end_off) | ||
199 | goto skip; | ||
200 | size = fd->keylength + fd->entrylength; | ||
201 | |||
202 | do { | ||
203 | data_off = hfs_bnode_read_u16(node, rec_off); | ||
204 | hfs_bnode_write_u16(node, rec_off + 2, data_off - size); | ||
205 | rec_off -= 2; | ||
206 | } while (rec_off >= end_off); | ||
207 | |||
208 | /* fill hole */ | ||
209 | hfs_bnode_move(node, fd->keyoffset, fd->keyoffset + size, | ||
210 | data_off - fd->keyoffset - size); | ||
211 | skip: | ||
212 | hfs_bnode_dump(node); | ||
213 | if (!fd->record) | ||
214 | hfs_brec_update_parent(fd); | ||
215 | return 0; | ||
216 | } | ||
217 | |||
218 | static struct hfs_bnode *hfs_bnode_split(struct hfs_find_data *fd) | ||
219 | { | ||
220 | struct hfs_btree *tree; | ||
221 | struct hfs_bnode *node, *new_node; | ||
222 | struct hfs_bnode_desc node_desc; | ||
223 | int num_recs, new_rec_off, new_off, old_rec_off; | ||
224 | int data_start, data_end, size; | ||
225 | |||
226 | tree = fd->tree; | ||
227 | node = fd->bnode; | ||
228 | new_node = hfs_bmap_alloc(tree); | ||
229 | if (IS_ERR(new_node)) | ||
230 | return new_node; | ||
231 | hfs_bnode_get(node); | ||
232 | dprint(DBG_BNODE_MOD, "split_nodes: %d - %d - %d\n", | ||
233 | node->this, new_node->this, node->next); | ||
234 | new_node->next = node->next; | ||
235 | new_node->prev = node->this; | ||
236 | new_node->parent = node->parent; | ||
237 | new_node->type = node->type; | ||
238 | new_node->height = node->height; | ||
239 | |||
240 | size = tree->node_size / 2 - node->num_recs * 2 - 14; | ||
241 | old_rec_off = tree->node_size - 4; | ||
242 | num_recs = 1; | ||
243 | for (;;) { | ||
244 | data_start = hfs_bnode_read_u16(node, old_rec_off); | ||
245 | if (data_start > size) | ||
246 | break; | ||
247 | old_rec_off -= 2; | ||
248 | if (++num_recs < node->num_recs) | ||
249 | continue; | ||
250 | /* panic? */ | ||
251 | hfs_bnode_put(node); | ||
252 | hfs_bnode_put(new_node); | ||
253 | return ERR_PTR(-ENOSPC); | ||
254 | } | ||
255 | |||
256 | if (fd->record + 1 < num_recs) { | ||
257 | /* new record is in the lower half, | ||
258 | * so leave some more space there | ||
259 | */ | ||
260 | old_rec_off += 2; | ||
261 | num_recs--; | ||
262 | data_start = hfs_bnode_read_u16(node, old_rec_off); | ||
263 | } else { | ||
264 | hfs_bnode_put(node); | ||
265 | hfs_bnode_get(new_node); | ||
266 | fd->bnode = new_node; | ||
267 | fd->record -= num_recs; | ||
268 | fd->keyoffset -= data_start - 14; | ||
269 | fd->entryoffset -= data_start - 14; | ||
270 | } | ||
271 | new_node->num_recs = node->num_recs - num_recs; | ||
272 | node->num_recs = num_recs; | ||
273 | |||
274 | new_rec_off = tree->node_size - 2; | ||
275 | new_off = 14; | ||
276 | size = data_start - new_off; | ||
277 | num_recs = new_node->num_recs; | ||
278 | data_end = data_start; | ||
279 | while (num_recs) { | ||
280 | hfs_bnode_write_u16(new_node, new_rec_off, new_off); | ||
281 | old_rec_off -= 2; | ||
282 | new_rec_off -= 2; | ||
283 | data_end = hfs_bnode_read_u16(node, old_rec_off); | ||
284 | new_off = data_end - size; | ||
285 | num_recs--; | ||
286 | } | ||
287 | hfs_bnode_write_u16(new_node, new_rec_off, new_off); | ||
288 | hfs_bnode_copy(new_node, 14, node, data_start, data_end - data_start); | ||
289 | |||
290 | /* update new bnode header */ | ||
291 | node_desc.next = cpu_to_be32(new_node->next); | ||
292 | node_desc.prev = cpu_to_be32(new_node->prev); | ||
293 | node_desc.type = new_node->type; | ||
294 | node_desc.height = new_node->height; | ||
295 | node_desc.num_recs = cpu_to_be16(new_node->num_recs); | ||
296 | node_desc.reserved = 0; | ||
297 | hfs_bnode_write(new_node, &node_desc, 0, sizeof(node_desc)); | ||
298 | |||
299 | /* update previous bnode header */ | ||
300 | node->next = new_node->this; | ||
301 | hfs_bnode_read(node, &node_desc, 0, sizeof(node_desc)); | ||
302 | node_desc.next = cpu_to_be32(node->next); | ||
303 | node_desc.num_recs = cpu_to_be16(node->num_recs); | ||
304 | hfs_bnode_write(node, &node_desc, 0, sizeof(node_desc)); | ||
305 | |||
306 | /* update next bnode header */ | ||
307 | if (new_node->next) { | ||
308 | struct hfs_bnode *next_node = hfs_bnode_find(tree, new_node->next); | ||
309 | next_node->prev = new_node->this; | ||
310 | hfs_bnode_read(next_node, &node_desc, 0, sizeof(node_desc)); | ||
311 | node_desc.prev = cpu_to_be32(next_node->prev); | ||
312 | hfs_bnode_write(next_node, &node_desc, 0, sizeof(node_desc)); | ||
313 | hfs_bnode_put(next_node); | ||
314 | } else if (node->this == tree->leaf_tail) { | ||
315 | /* if there is no next node, this might be the new tail */ | ||
316 | tree->leaf_tail = new_node->this; | ||
317 | mark_inode_dirty(tree->inode); | ||
318 | } | ||
319 | |||
320 | hfs_bnode_dump(node); | ||
321 | hfs_bnode_dump(new_node); | ||
322 | hfs_bnode_put(node); | ||
323 | |||
324 | return new_node; | ||
325 | } | ||
326 | |||
327 | static int hfs_brec_update_parent(struct hfs_find_data *fd) | ||
328 | { | ||
329 | struct hfs_btree *tree; | ||
330 | struct hfs_bnode *node, *new_node, *parent; | ||
331 | int newkeylen, diff; | ||
332 | int rec, rec_off, end_rec_off; | ||
333 | int start_off, end_off; | ||
334 | |||
335 | tree = fd->tree; | ||
336 | node = fd->bnode; | ||
337 | new_node = NULL; | ||
338 | if (!node->parent) | ||
339 | return 0; | ||
340 | |||
341 | again: | ||
342 | parent = hfs_bnode_find(tree, node->parent); | ||
343 | if (IS_ERR(parent)) | ||
344 | return PTR_ERR(parent); | ||
345 | __hfs_brec_find(parent, fd); | ||
346 | hfs_bnode_dump(parent); | ||
347 | rec = fd->record; | ||
348 | |||
349 | /* size difference between old and new key */ | ||
350 | if (tree->attributes & HFS_TREE_VARIDXKEYS) | ||
351 | newkeylen = (hfs_bnode_read_u8(node, 14) | 1) + 1; | ||
352 | else | ||
353 | fd->keylength = newkeylen = tree->max_key_len + 1; | ||
354 | dprint(DBG_BNODE_MOD, "update_rec: %d, %d, %d\n", rec, fd->keylength, newkeylen); | ||
355 | |||
356 | rec_off = tree->node_size - (rec + 2) * 2; | ||
357 | end_rec_off = tree->node_size - (parent->num_recs + 1) * 2; | ||
358 | diff = newkeylen - fd->keylength; | ||
359 | if (!diff) | ||
360 | goto skip; | ||
361 | if (diff > 0) { | ||
362 | end_off = hfs_bnode_read_u16(parent, end_rec_off); | ||
363 | if (end_rec_off - end_off < diff) { | ||
364 | |||
365 | printk("splitting index node...\n"); | ||
366 | fd->bnode = parent; | ||
367 | new_node = hfs_bnode_split(fd); | ||
368 | if (IS_ERR(new_node)) | ||
369 | return PTR_ERR(new_node); | ||
370 | parent = fd->bnode; | ||
371 | rec = fd->record; | ||
372 | rec_off = tree->node_size - (rec + 2) * 2; | ||
373 | end_rec_off = tree->node_size - (parent->num_recs + 1) * 2; | ||
374 | } | ||
375 | } | ||
376 | |||
377 | end_off = start_off = hfs_bnode_read_u16(parent, rec_off); | ||
378 | hfs_bnode_write_u16(parent, rec_off, start_off + diff); | ||
379 | start_off -= 4; /* move previous cnid too */ | ||
380 | |||
381 | while (rec_off > end_rec_off) { | ||
382 | rec_off -= 2; | ||
383 | end_off = hfs_bnode_read_u16(parent, rec_off); | ||
384 | hfs_bnode_write_u16(parent, rec_off, end_off + diff); | ||
385 | } | ||
386 | hfs_bnode_move(parent, start_off + diff, start_off, | ||
387 | end_off - start_off); | ||
388 | skip: | ||
389 | hfs_bnode_copy(parent, fd->keyoffset, node, 14, newkeylen); | ||
390 | if (!(tree->attributes & HFS_TREE_VARIDXKEYS)) | ||
391 | hfs_bnode_write_u8(parent, fd->keyoffset, newkeylen - 1); | ||
392 | hfs_bnode_dump(parent); | ||
393 | |||
394 | hfs_bnode_put(node); | ||
395 | node = parent; | ||
396 | |||
397 | if (new_node) { | ||
398 | __be32 cnid; | ||
399 | |||
400 | fd->bnode = hfs_bnode_find(tree, new_node->parent); | ||
401 | /* create index key and entry */ | ||
402 | hfs_bnode_read_key(new_node, fd->search_key, 14); | ||
403 | cnid = cpu_to_be32(new_node->this); | ||
404 | |||
405 | __hfs_brec_find(fd->bnode, fd); | ||
406 | hfs_brec_insert(fd, &cnid, sizeof(cnid)); | ||
407 | hfs_bnode_put(fd->bnode); | ||
408 | hfs_bnode_put(new_node); | ||
409 | |||
410 | if (!rec) { | ||
411 | if (new_node == node) | ||
412 | goto out; | ||
413 | /* restore search_key */ | ||
414 | hfs_bnode_read_key(node, fd->search_key, 14); | ||
415 | } | ||
416 | } | ||
417 | |||
418 | if (!rec && node->parent) | ||
419 | goto again; | ||
420 | out: | ||
421 | fd->bnode = node; | ||
422 | return 0; | ||
423 | } | ||
424 | |||
425 | static int hfs_btree_inc_height(struct hfs_btree *tree) | ||
426 | { | ||
427 | struct hfs_bnode *node, *new_node; | ||
428 | struct hfs_bnode_desc node_desc; | ||
429 | int key_size, rec; | ||
430 | __be32 cnid; | ||
431 | |||
432 | node = NULL; | ||
433 | if (tree->root) { | ||
434 | node = hfs_bnode_find(tree, tree->root); | ||
435 | if (IS_ERR(node)) | ||
436 | return PTR_ERR(node); | ||
437 | } | ||
438 | new_node = hfs_bmap_alloc(tree); | ||
439 | if (IS_ERR(new_node)) { | ||
440 | hfs_bnode_put(node); | ||
441 | return PTR_ERR(new_node); | ||
442 | } | ||
443 | |||
444 | tree->root = new_node->this; | ||
445 | if (!tree->depth) { | ||
446 | tree->leaf_head = tree->leaf_tail = new_node->this; | ||
447 | new_node->type = HFS_NODE_LEAF; | ||
448 | new_node->num_recs = 0; | ||
449 | } else { | ||
450 | new_node->type = HFS_NODE_INDEX; | ||
451 | new_node->num_recs = 1; | ||
452 | } | ||
453 | new_node->parent = 0; | ||
454 | new_node->next = 0; | ||
455 | new_node->prev = 0; | ||
456 | new_node->height = ++tree->depth; | ||
457 | |||
458 | node_desc.next = cpu_to_be32(new_node->next); | ||
459 | node_desc.prev = cpu_to_be32(new_node->prev); | ||
460 | node_desc.type = new_node->type; | ||
461 | node_desc.height = new_node->height; | ||
462 | node_desc.num_recs = cpu_to_be16(new_node->num_recs); | ||
463 | node_desc.reserved = 0; | ||
464 | hfs_bnode_write(new_node, &node_desc, 0, sizeof(node_desc)); | ||
465 | |||
466 | rec = tree->node_size - 2; | ||
467 | hfs_bnode_write_u16(new_node, rec, 14); | ||
468 | |||
469 | if (node) { | ||
470 | /* insert old root idx into new root */ | ||
471 | node->parent = tree->root; | ||
472 | if (node->type == HFS_NODE_LEAF || | ||
473 | tree->attributes & HFS_TREE_VARIDXKEYS) | ||
474 | key_size = hfs_bnode_read_u8(node, 14) + 1; | ||
475 | else | ||
476 | key_size = tree->max_key_len + 1; | ||
477 | hfs_bnode_copy(new_node, 14, node, 14, key_size); | ||
478 | |||
479 | if (!(tree->attributes & HFS_TREE_VARIDXKEYS)) { | ||
480 | key_size = tree->max_key_len + 1; | ||
481 | hfs_bnode_write_u8(new_node, 14, tree->max_key_len); | ||
482 | } | ||
483 | key_size = (key_size + 1) & -2; | ||
484 | cnid = cpu_to_be32(node->this); | ||
485 | hfs_bnode_write(new_node, &cnid, 14 + key_size, 4); | ||
486 | |||
487 | rec -= 2; | ||
488 | hfs_bnode_write_u16(new_node, rec, 14 + key_size + 4); | ||
489 | |||
490 | hfs_bnode_put(node); | ||
491 | } | ||
492 | hfs_bnode_put(new_node); | ||
493 | mark_inode_dirty(tree->inode); | ||
494 | |||
495 | return 0; | ||
496 | } | ||
diff --git a/fs/hfs/btree.c b/fs/hfs/btree.c new file mode 100644 index 000000000000..394725efa1c8 --- /dev/null +++ b/fs/hfs/btree.c | |||
@@ -0,0 +1,327 @@ | |||
1 | /* | ||
2 | * linux/fs/hfs/btree.c | ||
3 | * | ||
4 | * Copyright (C) 2001 | ||
5 | * Brad Boyer (flar@allandria.com) | ||
6 | * (C) 2003 Ardis Technologies <roman@ardistech.com> | ||
7 | * | ||
8 | * Handle opening/closing btree | ||
9 | */ | ||
10 | |||
11 | #include <linux/pagemap.h> | ||
12 | |||
13 | #include "btree.h" | ||
14 | |||
15 | /* Get a reference to a B*Tree and do some initial checks */ | ||
16 | struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id, btree_keycmp keycmp) | ||
17 | { | ||
18 | struct hfs_btree *tree; | ||
19 | struct hfs_btree_header_rec *head; | ||
20 | struct address_space *mapping; | ||
21 | struct page *page; | ||
22 | unsigned int size; | ||
23 | |||
24 | tree = kmalloc(sizeof(*tree), GFP_KERNEL); | ||
25 | if (!tree) | ||
26 | return NULL; | ||
27 | memset(tree, 0, sizeof(*tree)); | ||
28 | |||
29 | init_MUTEX(&tree->tree_lock); | ||
30 | spin_lock_init(&tree->hash_lock); | ||
31 | /* Set the correct compare function */ | ||
32 | tree->sb = sb; | ||
33 | tree->cnid = id; | ||
34 | tree->keycmp = keycmp; | ||
35 | |||
36 | tree->inode = iget_locked(sb, id); | ||
37 | if (!tree->inode) | ||
38 | goto free_tree; | ||
39 | if (!(tree->inode->i_state & I_NEW)) | ||
40 | BUG(); | ||
41 | { | ||
42 | struct hfs_mdb *mdb = HFS_SB(sb)->mdb; | ||
43 | HFS_I(tree->inode)->flags = 0; | ||
44 | init_MUTEX(&HFS_I(tree->inode)->extents_lock); | ||
45 | switch (id) { | ||
46 | case HFS_EXT_CNID: | ||
47 | hfs_inode_read_fork(tree->inode, mdb->drXTExtRec, mdb->drXTFlSize, | ||
48 | mdb->drXTFlSize, be32_to_cpu(mdb->drXTClpSiz)); | ||
49 | tree->inode->i_mapping->a_ops = &hfs_btree_aops; | ||
50 | break; | ||
51 | case HFS_CAT_CNID: | ||
52 | hfs_inode_read_fork(tree->inode, mdb->drCTExtRec, mdb->drCTFlSize, | ||
53 | mdb->drCTFlSize, be32_to_cpu(mdb->drCTClpSiz)); | ||
54 | tree->inode->i_mapping->a_ops = &hfs_btree_aops; | ||
55 | break; | ||
56 | default: | ||
57 | BUG(); | ||
58 | } | ||
59 | } | ||
60 | unlock_new_inode(tree->inode); | ||
61 | |||
62 | mapping = tree->inode->i_mapping; | ||
63 | page = read_cache_page(mapping, 0, (filler_t *)mapping->a_ops->readpage, NULL); | ||
64 | if (IS_ERR(page)) | ||
65 | goto free_tree; | ||
66 | |||
67 | /* Load the header */ | ||
68 | head = (struct hfs_btree_header_rec *)(kmap(page) + sizeof(struct hfs_bnode_desc)); | ||
69 | tree->root = be32_to_cpu(head->root); | ||
70 | tree->leaf_count = be32_to_cpu(head->leaf_count); | ||
71 | tree->leaf_head = be32_to_cpu(head->leaf_head); | ||
72 | tree->leaf_tail = be32_to_cpu(head->leaf_tail); | ||
73 | tree->node_count = be32_to_cpu(head->node_count); | ||
74 | tree->free_nodes = be32_to_cpu(head->free_nodes); | ||
75 | tree->attributes = be32_to_cpu(head->attributes); | ||
76 | tree->node_size = be16_to_cpu(head->node_size); | ||
77 | tree->max_key_len = be16_to_cpu(head->max_key_len); | ||
78 | tree->depth = be16_to_cpu(head->depth); | ||
79 | |||
80 | size = tree->node_size; | ||
81 | if (!size || size & (size - 1)) | ||
82 | goto fail_page; | ||
83 | if (!tree->node_count) | ||
84 | goto fail_page; | ||
85 | tree->node_size_shift = ffs(size) - 1; | ||
86 | tree->pages_per_bnode = (tree->node_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; | ||
87 | |||
88 | kunmap(page); | ||
89 | page_cache_release(page); | ||
90 | return tree; | ||
91 | |||
92 | fail_page: | ||
93 | tree->inode->i_mapping->a_ops = &hfs_aops; | ||
94 | page_cache_release(page); | ||
95 | free_tree: | ||
96 | iput(tree->inode); | ||
97 | kfree(tree); | ||
98 | return NULL; | ||
99 | } | ||
100 | |||
101 | /* Release resources used by a btree */ | ||
102 | void hfs_btree_close(struct hfs_btree *tree) | ||
103 | { | ||
104 | struct hfs_bnode *node; | ||
105 | int i; | ||
106 | |||
107 | if (!tree) | ||
108 | return; | ||
109 | |||
110 | for (i = 0; i < NODE_HASH_SIZE; i++) { | ||
111 | while ((node = tree->node_hash[i])) { | ||
112 | tree->node_hash[i] = node->next_hash; | ||
113 | if (atomic_read(&node->refcnt)) | ||
114 | printk("HFS: node %d:%d still has %d user(s)!\n", | ||
115 | node->tree->cnid, node->this, atomic_read(&node->refcnt)); | ||
116 | hfs_bnode_free(node); | ||
117 | tree->node_hash_cnt--; | ||
118 | } | ||
119 | } | ||
120 | iput(tree->inode); | ||
121 | kfree(tree); | ||
122 | } | ||
123 | |||
124 | void hfs_btree_write(struct hfs_btree *tree) | ||
125 | { | ||
126 | struct hfs_btree_header_rec *head; | ||
127 | struct hfs_bnode *node; | ||
128 | struct page *page; | ||
129 | |||
130 | node = hfs_bnode_find(tree, 0); | ||
131 | if (IS_ERR(node)) | ||
132 | /* panic? */ | ||
133 | return; | ||
134 | /* Load the header */ | ||
135 | page = node->page[0]; | ||
136 | head = (struct hfs_btree_header_rec *)(kmap(page) + sizeof(struct hfs_bnode_desc)); | ||
137 | |||
138 | head->root = cpu_to_be32(tree->root); | ||
139 | head->leaf_count = cpu_to_be32(tree->leaf_count); | ||
140 | head->leaf_head = cpu_to_be32(tree->leaf_head); | ||
141 | head->leaf_tail = cpu_to_be32(tree->leaf_tail); | ||
142 | head->node_count = cpu_to_be32(tree->node_count); | ||
143 | head->free_nodes = cpu_to_be32(tree->free_nodes); | ||
144 | head->attributes = cpu_to_be32(tree->attributes); | ||
145 | head->depth = cpu_to_be16(tree->depth); | ||
146 | |||
147 | kunmap(page); | ||
148 | set_page_dirty(page); | ||
149 | hfs_bnode_put(node); | ||
150 | } | ||
151 | |||
152 | static struct hfs_bnode *hfs_bmap_new_bmap(struct hfs_bnode *prev, u32 idx) | ||
153 | { | ||
154 | struct hfs_btree *tree = prev->tree; | ||
155 | struct hfs_bnode *node; | ||
156 | struct hfs_bnode_desc desc; | ||
157 | __be32 cnid; | ||
158 | |||
159 | node = hfs_bnode_create(tree, idx); | ||
160 | if (IS_ERR(node)) | ||
161 | return node; | ||
162 | |||
163 | if (!tree->free_nodes) | ||
164 | panic("FIXME!!!"); | ||
165 | tree->free_nodes--; | ||
166 | prev->next = idx; | ||
167 | cnid = cpu_to_be32(idx); | ||
168 | hfs_bnode_write(prev, &cnid, offsetof(struct hfs_bnode_desc, next), 4); | ||
169 | |||
170 | node->type = HFS_NODE_MAP; | ||
171 | node->num_recs = 1; | ||
172 | hfs_bnode_clear(node, 0, tree->node_size); | ||
173 | desc.next = 0; | ||
174 | desc.prev = 0; | ||
175 | desc.type = HFS_NODE_MAP; | ||
176 | desc.height = 0; | ||
177 | desc.num_recs = cpu_to_be16(1); | ||
178 | desc.reserved = 0; | ||
179 | hfs_bnode_write(node, &desc, 0, sizeof(desc)); | ||
180 | hfs_bnode_write_u16(node, 14, 0x8000); | ||
181 | hfs_bnode_write_u16(node, tree->node_size - 2, 14); | ||
182 | hfs_bnode_write_u16(node, tree->node_size - 4, tree->node_size - 6); | ||
183 | |||
184 | return node; | ||
185 | } | ||
186 | |||
187 | struct hfs_bnode *hfs_bmap_alloc(struct hfs_btree *tree) | ||
188 | { | ||
189 | struct hfs_bnode *node, *next_node; | ||
190 | struct page **pagep; | ||
191 | u32 nidx, idx; | ||
192 | u16 off, len; | ||
193 | u8 *data, byte, m; | ||
194 | int i; | ||
195 | |||
196 | while (!tree->free_nodes) { | ||
197 | struct inode *inode = tree->inode; | ||
198 | u32 count; | ||
199 | int res; | ||
200 | |||
201 | res = hfs_extend_file(inode); | ||
202 | if (res) | ||
203 | return ERR_PTR(res); | ||
204 | HFS_I(inode)->phys_size = inode->i_size = | ||
205 | (loff_t)HFS_I(inode)->alloc_blocks * | ||
206 | HFS_SB(tree->sb)->alloc_blksz; | ||
207 | HFS_I(inode)->fs_blocks = inode->i_size >> | ||
208 | tree->sb->s_blocksize_bits; | ||
209 | inode_set_bytes(inode, inode->i_size); | ||
210 | count = inode->i_size >> tree->node_size_shift; | ||
211 | tree->free_nodes = count - tree->node_count; | ||
212 | tree->node_count = count; | ||
213 | } | ||
214 | |||
215 | nidx = 0; | ||
216 | node = hfs_bnode_find(tree, nidx); | ||
217 | if (IS_ERR(node)) | ||
218 | return node; | ||
219 | len = hfs_brec_lenoff(node, 2, &off); | ||
220 | |||
221 | off += node->page_offset; | ||
222 | pagep = node->page + (off >> PAGE_CACHE_SHIFT); | ||
223 | data = kmap(*pagep); | ||
224 | off &= ~PAGE_CACHE_MASK; | ||
225 | idx = 0; | ||
226 | |||
227 | for (;;) { | ||
228 | while (len) { | ||
229 | byte = data[off]; | ||
230 | if (byte != 0xff) { | ||
231 | for (m = 0x80, i = 0; i < 8; m >>= 1, i++) { | ||
232 | if (!(byte & m)) { | ||
233 | idx += i; | ||
234 | data[off] |= m; | ||
235 | set_page_dirty(*pagep); | ||
236 | kunmap(*pagep); | ||
237 | tree->free_nodes--; | ||
238 | mark_inode_dirty(tree->inode); | ||
239 | hfs_bnode_put(node); | ||
240 | return hfs_bnode_create(tree, idx); | ||
241 | } | ||
242 | } | ||
243 | } | ||
244 | if (++off >= PAGE_CACHE_SIZE) { | ||
245 | kunmap(*pagep); | ||
246 | data = kmap(*++pagep); | ||
247 | off = 0; | ||
248 | } | ||
249 | idx += 8; | ||
250 | len--; | ||
251 | } | ||
252 | kunmap(*pagep); | ||
253 | nidx = node->next; | ||
254 | if (!nidx) { | ||
255 | printk("create new bmap node...\n"); | ||
256 | next_node = hfs_bmap_new_bmap(node, idx); | ||
257 | } else | ||
258 | next_node = hfs_bnode_find(tree, nidx); | ||
259 | hfs_bnode_put(node); | ||
260 | if (IS_ERR(next_node)) | ||
261 | return next_node; | ||
262 | node = next_node; | ||
263 | |||
264 | len = hfs_brec_lenoff(node, 0, &off); | ||
265 | off += node->page_offset; | ||
266 | pagep = node->page + (off >> PAGE_CACHE_SHIFT); | ||
267 | data = kmap(*pagep); | ||
268 | off &= ~PAGE_CACHE_MASK; | ||
269 | } | ||
270 | } | ||
271 | |||
272 | void hfs_bmap_free(struct hfs_bnode *node) | ||
273 | { | ||
274 | struct hfs_btree *tree; | ||
275 | struct page *page; | ||
276 | u16 off, len; | ||
277 | u32 nidx; | ||
278 | u8 *data, byte, m; | ||
279 | |||
280 | dprint(DBG_BNODE_MOD, "btree_free_node: %u\n", node->this); | ||
281 | tree = node->tree; | ||
282 | nidx = node->this; | ||
283 | node = hfs_bnode_find(tree, 0); | ||
284 | if (IS_ERR(node)) | ||
285 | return; | ||
286 | len = hfs_brec_lenoff(node, 2, &off); | ||
287 | while (nidx >= len * 8) { | ||
288 | u32 i; | ||
289 | |||
290 | nidx -= len * 8; | ||
291 | i = node->next; | ||
292 | hfs_bnode_put(node); | ||
293 | if (!i) { | ||
294 | /* panic */; | ||
295 | printk("HFS: unable to free bnode %u. bmap not found!\n", node->this); | ||
296 | return; | ||
297 | } | ||
298 | node = hfs_bnode_find(tree, i); | ||
299 | if (IS_ERR(node)) | ||
300 | return; | ||
301 | if (node->type != HFS_NODE_MAP) { | ||
302 | /* panic */; | ||
303 | printk("HFS: invalid bmap found! (%u,%d)\n", node->this, node->type); | ||
304 | hfs_bnode_put(node); | ||
305 | return; | ||
306 | } | ||
307 | len = hfs_brec_lenoff(node, 0, &off); | ||
308 | } | ||
309 | off += node->page_offset + nidx / 8; | ||
310 | page = node->page[off >> PAGE_CACHE_SHIFT]; | ||
311 | data = kmap(page); | ||
312 | off &= ~PAGE_CACHE_MASK; | ||
313 | m = 1 << (~nidx & 7); | ||
314 | byte = data[off]; | ||
315 | if (!(byte & m)) { | ||
316 | printk("HFS: trying to free free bnode %u(%d)\n", node->this, node->type); | ||
317 | kunmap(page); | ||
318 | hfs_bnode_put(node); | ||
319 | return; | ||
320 | } | ||
321 | data[off] = byte & ~m; | ||
322 | set_page_dirty(page); | ||
323 | kunmap(page); | ||
324 | hfs_bnode_put(node); | ||
325 | tree->free_nodes++; | ||
326 | mark_inode_dirty(tree->inode); | ||
327 | } | ||
diff --git a/fs/hfs/btree.h b/fs/hfs/btree.h new file mode 100644 index 000000000000..cc51905ac21d --- /dev/null +++ b/fs/hfs/btree.h | |||
@@ -0,0 +1,168 @@ | |||
1 | /* | ||
2 | * linux/fs/hfs/btree.h | ||
3 | * | ||
4 | * Copyright (C) 2001 | ||
5 | * Brad Boyer (flar@allandria.com) | ||
6 | * (C) 2003 Ardis Technologies <roman@ardistech.com> | ||
7 | */ | ||
8 | |||
9 | #include "hfs_fs.h" | ||
10 | |||
11 | typedef int (*btree_keycmp)(const btree_key *, const btree_key *); | ||
12 | |||
13 | #define NODE_HASH_SIZE 256 | ||
14 | |||
15 | /* A HFS BTree held in memory */ | ||
16 | struct hfs_btree { | ||
17 | struct super_block *sb; | ||
18 | struct inode *inode; | ||
19 | btree_keycmp keycmp; | ||
20 | |||
21 | u32 cnid; | ||
22 | u32 root; | ||
23 | u32 leaf_count; | ||
24 | u32 leaf_head; | ||
25 | u32 leaf_tail; | ||
26 | u32 node_count; | ||
27 | u32 free_nodes; | ||
28 | u32 attributes; | ||
29 | |||
30 | unsigned int node_size; | ||
31 | unsigned int node_size_shift; | ||
32 | unsigned int max_key_len; | ||
33 | unsigned int depth; | ||
34 | |||
35 | //unsigned int map1_size, map_size; | ||
36 | struct semaphore tree_lock; | ||
37 | |||
38 | unsigned int pages_per_bnode; | ||
39 | spinlock_t hash_lock; | ||
40 | struct hfs_bnode *node_hash[NODE_HASH_SIZE]; | ||
41 | int node_hash_cnt; | ||
42 | }; | ||
43 | |||
44 | /* A HFS BTree node in memory */ | ||
45 | struct hfs_bnode { | ||
46 | struct hfs_btree *tree; | ||
47 | |||
48 | u32 prev; | ||
49 | u32 this; | ||
50 | u32 next; | ||
51 | u32 parent; | ||
52 | |||
53 | u16 num_recs; | ||
54 | u8 type; | ||
55 | u8 height; | ||
56 | |||
57 | struct hfs_bnode *next_hash; | ||
58 | unsigned long flags; | ||
59 | wait_queue_head_t lock_wq; | ||
60 | atomic_t refcnt; | ||
61 | unsigned int page_offset; | ||
62 | struct page *page[0]; | ||
63 | }; | ||
64 | |||
65 | #define HFS_BNODE_ERROR 0 | ||
66 | #define HFS_BNODE_NEW 1 | ||
67 | #define HFS_BNODE_DELETED 2 | ||
68 | |||
69 | struct hfs_find_data { | ||
70 | btree_key *key; | ||
71 | btree_key *search_key; | ||
72 | struct hfs_btree *tree; | ||
73 | struct hfs_bnode *bnode; | ||
74 | int record; | ||
75 | int keyoffset, keylength; | ||
76 | int entryoffset, entrylength; | ||
77 | }; | ||
78 | |||
79 | |||
80 | /* btree.c */ | ||
81 | extern struct hfs_btree *hfs_btree_open(struct super_block *, u32, btree_keycmp); | ||
82 | extern void hfs_btree_close(struct hfs_btree *); | ||
83 | extern void hfs_btree_write(struct hfs_btree *); | ||
84 | extern struct hfs_bnode * hfs_bmap_alloc(struct hfs_btree *); | ||
85 | extern void hfs_bmap_free(struct hfs_bnode *node); | ||
86 | |||
87 | /* bnode.c */ | ||
88 | extern void hfs_bnode_read(struct hfs_bnode *, void *, int, int); | ||
89 | extern u16 hfs_bnode_read_u16(struct hfs_bnode *, int); | ||
90 | extern u8 hfs_bnode_read_u8(struct hfs_bnode *, int); | ||
91 | extern void hfs_bnode_read_key(struct hfs_bnode *, void *, int); | ||
92 | extern void hfs_bnode_write(struct hfs_bnode *, void *, int, int); | ||
93 | extern void hfs_bnode_write_u16(struct hfs_bnode *, int, u16); | ||
94 | extern void hfs_bnode_write_u8(struct hfs_bnode *, int, u8); | ||
95 | extern void hfs_bnode_clear(struct hfs_bnode *, int, int); | ||
96 | extern void hfs_bnode_copy(struct hfs_bnode *, int, | ||
97 | struct hfs_bnode *, int, int); | ||
98 | extern void hfs_bnode_move(struct hfs_bnode *, int, int, int); | ||
99 | extern void hfs_bnode_dump(struct hfs_bnode *); | ||
100 | extern void hfs_bnode_unlink(struct hfs_bnode *); | ||
101 | extern struct hfs_bnode *hfs_bnode_findhash(struct hfs_btree *, u32); | ||
102 | extern struct hfs_bnode *hfs_bnode_find(struct hfs_btree *, u32); | ||
103 | extern void hfs_bnode_unhash(struct hfs_bnode *); | ||
104 | extern void hfs_bnode_free(struct hfs_bnode *); | ||
105 | extern struct hfs_bnode *hfs_bnode_create(struct hfs_btree *, u32); | ||
106 | extern void hfs_bnode_get(struct hfs_bnode *); | ||
107 | extern void hfs_bnode_put(struct hfs_bnode *); | ||
108 | |||
109 | /* brec.c */ | ||
110 | extern u16 hfs_brec_lenoff(struct hfs_bnode *, u16, u16 *); | ||
111 | extern u16 hfs_brec_keylen(struct hfs_bnode *, u16); | ||
112 | extern int hfs_brec_insert(struct hfs_find_data *, void *, int); | ||
113 | extern int hfs_brec_remove(struct hfs_find_data *); | ||
114 | |||
115 | /* bfind.c */ | ||
116 | extern int hfs_find_init(struct hfs_btree *, struct hfs_find_data *); | ||
117 | extern void hfs_find_exit(struct hfs_find_data *); | ||
118 | extern int __hfs_brec_find(struct hfs_bnode *, struct hfs_find_data *); | ||
119 | extern int hfs_brec_find(struct hfs_find_data *); | ||
120 | extern int hfs_brec_read(struct hfs_find_data *, void *, int); | ||
121 | extern int hfs_brec_goto(struct hfs_find_data *, int); | ||
122 | |||
123 | |||
124 | struct hfs_bnode_desc { | ||
125 | __be32 next; /* (V) Number of the next node at this level */ | ||
126 | __be32 prev; /* (V) Number of the prev node at this level */ | ||
127 | u8 type; /* (F) The type of node */ | ||
128 | u8 height; /* (F) The level of this node (leaves=1) */ | ||
129 | __be16 num_recs; /* (V) The number of records in this node */ | ||
130 | u16 reserved; | ||
131 | } __packed; | ||
132 | |||
133 | #define HFS_NODE_INDEX 0x00 /* An internal (index) node */ | ||
134 | #define HFS_NODE_HEADER 0x01 /* The tree header node (node 0) */ | ||
135 | #define HFS_NODE_MAP 0x02 /* Holds part of the bitmap of used nodes */ | ||
136 | #define HFS_NODE_LEAF 0xFF /* A leaf (ndNHeight==1) node */ | ||
137 | |||
138 | struct hfs_btree_header_rec { | ||
139 | __be16 depth; /* (V) The number of levels in this B-tree */ | ||
140 | __be32 root; /* (V) The node number of the root node */ | ||
141 | __be32 leaf_count; /* (V) The number of leaf records */ | ||
142 | __be32 leaf_head; /* (V) The number of the first leaf node */ | ||
143 | __be32 leaf_tail; /* (V) The number of the last leaf node */ | ||
144 | __be16 node_size; /* (F) The number of bytes in a node (=512) */ | ||
145 | __be16 max_key_len; /* (F) The length of a key in an index node */ | ||
146 | __be32 node_count; /* (V) The total number of nodes */ | ||
147 | __be32 free_nodes; /* (V) The number of unused nodes */ | ||
148 | u16 reserved1; | ||
149 | __be32 clump_size; /* (F) clump size. not usually used. */ | ||
150 | u8 btree_type; /* (F) BTree type */ | ||
151 | u8 reserved2; | ||
152 | __be32 attributes; /* (F) attributes */ | ||
153 | u32 reserved3[16]; | ||
154 | } __packed; | ||
155 | |||
156 | #define HFS_NODE_INDEX 0x00 /* An internal (index) node */ | ||
157 | #define HFS_NODE_HEADER 0x01 /* The tree header node (node 0) */ | ||
158 | #define HFS_NODE_MAP 0x02 /* Holds part of the bitmap of used nodes */ | ||
159 | #define HFS_NODE_LEAF 0xFF /* A leaf (ndNHeight==1) node */ | ||
160 | |||
161 | #define BTREE_ATTR_BADCLOSE 0x00000001 /* b-tree not closed properly. not | ||
162 | used by hfsplus. */ | ||
163 | #define HFS_TREE_BIGKEYS 0x00000002 /* key length is u16 instead of u8. | ||
164 | used by hfsplus. */ | ||
165 | #define HFS_TREE_VARIDXKEYS 0x00000004 /* variable key length instead of | ||
166 | max key length. use din catalog | ||
167 | b-tree but not in extents | ||
168 | b-tree (hfsplus). */ | ||
diff --git a/fs/hfs/catalog.c b/fs/hfs/catalog.c new file mode 100644 index 000000000000..65dedefcabfc --- /dev/null +++ b/fs/hfs/catalog.c | |||
@@ -0,0 +1,347 @@ | |||
1 | /* | ||
2 | * linux/fs/hfs/catalog.c | ||
3 | * | ||
4 | * Copyright (C) 1995-1997 Paul H. Hargrove | ||
5 | * (C) 2003 Ardis Technologies <roman@ardistech.com> | ||
6 | * This file may be distributed under the terms of the GNU General Public License. | ||
7 | * | ||
8 | * This file contains the functions related to the catalog B-tree. | ||
9 | * | ||
10 | * Cache code shamelessly stolen from | ||
11 | * linux/fs/inode.c Copyright (C) 1991, 1992 Linus Torvalds | ||
12 | * re-shamelessly stolen Copyright (C) 1997 Linus Torvalds | ||
13 | */ | ||
14 | |||
15 | #include "hfs_fs.h" | ||
16 | #include "btree.h" | ||
17 | |||
18 | /* | ||
19 | * hfs_cat_build_key() | ||
20 | * | ||
21 | * Given the ID of the parent and the name build a search key. | ||
22 | */ | ||
23 | void hfs_cat_build_key(btree_key *key, u32 parent, struct qstr *name) | ||
24 | { | ||
25 | key->cat.reserved = 0; | ||
26 | key->cat.ParID = cpu_to_be32(parent); | ||
27 | if (name) { | ||
28 | hfs_triv2mac(&key->cat.CName, name); | ||
29 | key->key_len = 6 + key->cat.CName.len; | ||
30 | } else { | ||
31 | memset(&key->cat.CName, 0, sizeof(struct hfs_name)); | ||
32 | key->key_len = 6; | ||
33 | } | ||
34 | } | ||
35 | |||
36 | static int hfs_cat_build_record(hfs_cat_rec *rec, u32 cnid, struct inode *inode) | ||
37 | { | ||
38 | __be32 mtime = hfs_mtime(); | ||
39 | |||
40 | memset(rec, 0, sizeof(*rec)); | ||
41 | if (S_ISDIR(inode->i_mode)) { | ||
42 | rec->type = HFS_CDR_DIR; | ||
43 | rec->dir.DirID = cpu_to_be32(cnid); | ||
44 | rec->dir.CrDat = mtime; | ||
45 | rec->dir.MdDat = mtime; | ||
46 | rec->dir.BkDat = 0; | ||
47 | rec->dir.UsrInfo.frView = cpu_to_be16(0xff); | ||
48 | return sizeof(struct hfs_cat_dir); | ||
49 | } else { | ||
50 | /* init some fields for the file record */ | ||
51 | rec->type = HFS_CDR_FIL; | ||
52 | rec->file.Flags = HFS_FIL_USED | HFS_FIL_THD; | ||
53 | if (!(inode->i_mode & S_IWUSR)) | ||
54 | rec->file.Flags |= HFS_FIL_LOCK; | ||
55 | rec->file.FlNum = cpu_to_be32(cnid); | ||
56 | rec->file.CrDat = mtime; | ||
57 | rec->file.MdDat = mtime; | ||
58 | rec->file.BkDat = 0; | ||
59 | rec->file.UsrWds.fdType = HFS_SB(inode->i_sb)->s_type; | ||
60 | rec->file.UsrWds.fdCreator = HFS_SB(inode->i_sb)->s_creator; | ||
61 | return sizeof(struct hfs_cat_file); | ||
62 | } | ||
63 | } | ||
64 | |||
65 | static int hfs_cat_build_thread(hfs_cat_rec *rec, int type, | ||
66 | u32 parentid, struct qstr *name) | ||
67 | { | ||
68 | rec->type = type; | ||
69 | memset(rec->thread.reserved, 0, sizeof(rec->thread.reserved)); | ||
70 | rec->thread.ParID = cpu_to_be32(parentid); | ||
71 | hfs_triv2mac(&rec->thread.CName, name); | ||
72 | return sizeof(struct hfs_cat_thread); | ||
73 | } | ||
74 | |||
75 | /* | ||
76 | * create_entry() | ||
77 | * | ||
78 | * Add a new file or directory to the catalog B-tree and | ||
79 | * return a (struct hfs_cat_entry) for it in '*result'. | ||
80 | */ | ||
81 | int hfs_cat_create(u32 cnid, struct inode *dir, struct qstr *str, struct inode *inode) | ||
82 | { | ||
83 | struct hfs_find_data fd; | ||
84 | struct super_block *sb; | ||
85 | union hfs_cat_rec entry; | ||
86 | int entry_size; | ||
87 | int err; | ||
88 | |||
89 | dprint(DBG_CAT_MOD, "create_cat: %s,%u(%d)\n", str->name, cnid, inode->i_nlink); | ||
90 | if (dir->i_size >= HFS_MAX_VALENCE) | ||
91 | return -ENOSPC; | ||
92 | |||
93 | sb = dir->i_sb; | ||
94 | hfs_find_init(HFS_SB(sb)->cat_tree, &fd); | ||
95 | |||
96 | hfs_cat_build_key(fd.search_key, cnid, NULL); | ||
97 | entry_size = hfs_cat_build_thread(&entry, S_ISDIR(inode->i_mode) ? | ||
98 | HFS_CDR_THD : HFS_CDR_FTH, | ||
99 | dir->i_ino, str); | ||
100 | err = hfs_brec_find(&fd); | ||
101 | if (err != -ENOENT) { | ||
102 | if (!err) | ||
103 | err = -EEXIST; | ||
104 | goto err2; | ||
105 | } | ||
106 | err = hfs_brec_insert(&fd, &entry, entry_size); | ||
107 | if (err) | ||
108 | goto err2; | ||
109 | |||
110 | hfs_cat_build_key(fd.search_key, dir->i_ino, str); | ||
111 | entry_size = hfs_cat_build_record(&entry, cnid, inode); | ||
112 | err = hfs_brec_find(&fd); | ||
113 | if (err != -ENOENT) { | ||
114 | /* panic? */ | ||
115 | if (!err) | ||
116 | err = -EEXIST; | ||
117 | goto err1; | ||
118 | } | ||
119 | err = hfs_brec_insert(&fd, &entry, entry_size); | ||
120 | if (err) | ||
121 | goto err1; | ||
122 | |||
123 | dir->i_size++; | ||
124 | dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC; | ||
125 | mark_inode_dirty(dir); | ||
126 | hfs_find_exit(&fd); | ||
127 | return 0; | ||
128 | |||
129 | err1: | ||
130 | hfs_cat_build_key(fd.search_key, cnid, NULL); | ||
131 | if (!hfs_brec_find(&fd)) | ||
132 | hfs_brec_remove(&fd); | ||
133 | err2: | ||
134 | hfs_find_exit(&fd); | ||
135 | return err; | ||
136 | } | ||
137 | |||
138 | /* | ||
139 | * hfs_cat_compare() | ||
140 | * | ||
141 | * Description: | ||
142 | * This is the comparison function used for the catalog B-tree. In | ||
143 | * comparing catalog B-tree entries, the parent id is the most | ||
144 | * significant field (compared as unsigned ints). The name field is | ||
145 | * the least significant (compared in "Macintosh lexical order", | ||
146 | * see hfs_strcmp() in string.c) | ||
147 | * Input Variable(s): | ||
148 | * struct hfs_cat_key *key1: pointer to the first key to compare | ||
149 | * struct hfs_cat_key *key2: pointer to the second key to compare | ||
150 | * Output Variable(s): | ||
151 | * NONE | ||
152 | * Returns: | ||
153 | * int: negative if key1<key2, positive if key1>key2, and 0 if key1==key2 | ||
154 | * Preconditions: | ||
155 | * key1 and key2 point to "valid" (struct hfs_cat_key)s. | ||
156 | * Postconditions: | ||
157 | * This function has no side-effects | ||
158 | */ | ||
159 | int hfs_cat_keycmp(const btree_key *key1, const btree_key *key2) | ||
160 | { | ||
161 | int retval; | ||
162 | |||
163 | retval = be32_to_cpu(key1->cat.ParID) - be32_to_cpu(key2->cat.ParID); | ||
164 | if (!retval) | ||
165 | retval = hfs_strcmp(key1->cat.CName.name, key1->cat.CName.len, | ||
166 | key2->cat.CName.name, key2->cat.CName.len); | ||
167 | |||
168 | return retval; | ||
169 | } | ||
170 | |||
171 | /* Try to get a catalog entry for given catalog id */ | ||
172 | // move to read_super??? | ||
173 | int hfs_cat_find_brec(struct super_block *sb, u32 cnid, | ||
174 | struct hfs_find_data *fd) | ||
175 | { | ||
176 | hfs_cat_rec rec; | ||
177 | int res, len, type; | ||
178 | |||
179 | hfs_cat_build_key(fd->search_key, cnid, NULL); | ||
180 | res = hfs_brec_read(fd, &rec, sizeof(rec)); | ||
181 | if (res) | ||
182 | return res; | ||
183 | |||
184 | type = rec.type; | ||
185 | if (type != HFS_CDR_THD && type != HFS_CDR_FTH) { | ||
186 | printk("HFS-fs: Found bad thread record in catalog\n"); | ||
187 | return -EIO; | ||
188 | } | ||
189 | |||
190 | fd->search_key->cat.ParID = rec.thread.ParID; | ||
191 | len = fd->search_key->cat.CName.len = rec.thread.CName.len; | ||
192 | memcpy(fd->search_key->cat.CName.name, rec.thread.CName.name, len); | ||
193 | return hfs_brec_find(fd); | ||
194 | } | ||
195 | |||
196 | |||
197 | /* | ||
198 | * hfs_cat_delete() | ||
199 | * | ||
200 | * Delete the indicated file or directory. | ||
201 | * The associated thread is also removed unless ('with_thread'==0). | ||
202 | */ | ||
203 | int hfs_cat_delete(u32 cnid, struct inode *dir, struct qstr *str) | ||
204 | { | ||
205 | struct super_block *sb; | ||
206 | struct hfs_find_data fd; | ||
207 | struct list_head *pos; | ||
208 | int res, type; | ||
209 | |||
210 | dprint(DBG_CAT_MOD, "delete_cat: %s,%u\n", str ? str->name : NULL, cnid); | ||
211 | sb = dir->i_sb; | ||
212 | hfs_find_init(HFS_SB(sb)->cat_tree, &fd); | ||
213 | |||
214 | hfs_cat_build_key(fd.search_key, dir->i_ino, str); | ||
215 | res = hfs_brec_find(&fd); | ||
216 | if (res) | ||
217 | goto out; | ||
218 | |||
219 | type = hfs_bnode_read_u8(fd.bnode, fd.entryoffset); | ||
220 | if (type == HFS_CDR_FIL) { | ||
221 | struct hfs_cat_file file; | ||
222 | hfs_bnode_read(fd.bnode, &file, fd.entryoffset, sizeof(file)); | ||
223 | if (be32_to_cpu(file.FlNum) == cnid) { | ||
224 | #if 0 | ||
225 | hfs_free_fork(sb, &file, HFS_FK_DATA); | ||
226 | #endif | ||
227 | hfs_free_fork(sb, &file, HFS_FK_RSRC); | ||
228 | } | ||
229 | } | ||
230 | |||
231 | list_for_each(pos, &HFS_I(dir)->open_dir_list) { | ||
232 | struct hfs_readdir_data *rd = | ||
233 | list_entry(pos, struct hfs_readdir_data, list); | ||
234 | if (fd.tree->keycmp(fd.search_key, (void *)&rd->key) < 0) | ||
235 | rd->file->f_pos--; | ||
236 | } | ||
237 | |||
238 | res = hfs_brec_remove(&fd); | ||
239 | if (res) | ||
240 | goto out; | ||
241 | |||
242 | hfs_cat_build_key(fd.search_key, cnid, NULL); | ||
243 | res = hfs_brec_find(&fd); | ||
244 | if (!res) { | ||
245 | res = hfs_brec_remove(&fd); | ||
246 | if (res) | ||
247 | goto out; | ||
248 | } | ||
249 | |||
250 | dir->i_size--; | ||
251 | dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC; | ||
252 | mark_inode_dirty(dir); | ||
253 | res = 0; | ||
254 | out: | ||
255 | hfs_find_exit(&fd); | ||
256 | |||
257 | return res; | ||
258 | } | ||
259 | |||
260 | /* | ||
261 | * hfs_cat_move() | ||
262 | * | ||
263 | * Rename a file or directory, possibly to a new directory. | ||
264 | * If the destination exists it is removed and a | ||
265 | * (struct hfs_cat_entry) for it is returned in '*result'. | ||
266 | */ | ||
267 | int hfs_cat_move(u32 cnid, struct inode *src_dir, struct qstr *src_name, | ||
268 | struct inode *dst_dir, struct qstr *dst_name) | ||
269 | { | ||
270 | struct super_block *sb; | ||
271 | struct hfs_find_data src_fd, dst_fd; | ||
272 | union hfs_cat_rec entry; | ||
273 | int entry_size, type; | ||
274 | int err; | ||
275 | |||
276 | dprint(DBG_CAT_MOD, "rename_cat: %u - %lu,%s - %lu,%s\n", cnid, src_dir->i_ino, src_name->name, | ||
277 | dst_dir->i_ino, dst_name->name); | ||
278 | sb = src_dir->i_sb; | ||
279 | hfs_find_init(HFS_SB(sb)->cat_tree, &src_fd); | ||
280 | dst_fd = src_fd; | ||
281 | |||
282 | /* find the old dir entry and read the data */ | ||
283 | hfs_cat_build_key(src_fd.search_key, src_dir->i_ino, src_name); | ||
284 | err = hfs_brec_find(&src_fd); | ||
285 | if (err) | ||
286 | goto out; | ||
287 | |||
288 | hfs_bnode_read(src_fd.bnode, &entry, src_fd.entryoffset, | ||
289 | src_fd.entrylength); | ||
290 | |||
291 | /* create new dir entry with the data from the old entry */ | ||
292 | hfs_cat_build_key(dst_fd.search_key, dst_dir->i_ino, dst_name); | ||
293 | err = hfs_brec_find(&dst_fd); | ||
294 | if (err != -ENOENT) { | ||
295 | if (!err) | ||
296 | err = -EEXIST; | ||
297 | goto out; | ||
298 | } | ||
299 | |||
300 | err = hfs_brec_insert(&dst_fd, &entry, src_fd.entrylength); | ||
301 | if (err) | ||
302 | goto out; | ||
303 | dst_dir->i_size++; | ||
304 | dst_dir->i_mtime = dst_dir->i_ctime = CURRENT_TIME_SEC; | ||
305 | mark_inode_dirty(dst_dir); | ||
306 | |||
307 | /* finally remove the old entry */ | ||
308 | hfs_cat_build_key(src_fd.search_key, src_dir->i_ino, src_name); | ||
309 | err = hfs_brec_find(&src_fd); | ||
310 | if (err) | ||
311 | goto out; | ||
312 | err = hfs_brec_remove(&src_fd); | ||
313 | if (err) | ||
314 | goto out; | ||
315 | src_dir->i_size--; | ||
316 | src_dir->i_mtime = src_dir->i_ctime = CURRENT_TIME_SEC; | ||
317 | mark_inode_dirty(src_dir); | ||
318 | |||
319 | type = entry.type; | ||
320 | if (type == HFS_CDR_FIL && !(entry.file.Flags & HFS_FIL_THD)) | ||
321 | goto out; | ||
322 | |||
323 | /* remove old thread entry */ | ||
324 | hfs_cat_build_key(src_fd.search_key, cnid, NULL); | ||
325 | err = hfs_brec_find(&src_fd); | ||
326 | if (err) | ||
327 | goto out; | ||
328 | err = hfs_brec_remove(&src_fd); | ||
329 | if (err) | ||
330 | goto out; | ||
331 | |||
332 | /* create new thread entry */ | ||
333 | hfs_cat_build_key(dst_fd.search_key, cnid, NULL); | ||
334 | entry_size = hfs_cat_build_thread(&entry, type == HFS_CDR_FIL ? HFS_CDR_FTH : HFS_CDR_THD, | ||
335 | dst_dir->i_ino, dst_name); | ||
336 | err = hfs_brec_find(&dst_fd); | ||
337 | if (err != -ENOENT) { | ||
338 | if (!err) | ||
339 | err = -EEXIST; | ||
340 | goto out; | ||
341 | } | ||
342 | err = hfs_brec_insert(&dst_fd, &entry, entry_size); | ||
343 | out: | ||
344 | hfs_bnode_put(dst_fd.bnode); | ||
345 | hfs_find_exit(&src_fd); | ||
346 | return err; | ||
347 | } | ||
diff --git a/fs/hfs/dir.c b/fs/hfs/dir.c new file mode 100644 index 000000000000..c55998262aed --- /dev/null +++ b/fs/hfs/dir.c | |||
@@ -0,0 +1,330 @@ | |||
1 | /* | ||
2 | * linux/fs/hfs/dir.c | ||
3 | * | ||
4 | * Copyright (C) 1995-1997 Paul H. Hargrove | ||
5 | * (C) 2003 Ardis Technologies <roman@ardistech.com> | ||
6 | * This file may be distributed under the terms of the GNU General Public License. | ||
7 | * | ||
8 | * This file contains directory-related functions independent of which | ||
9 | * scheme is being used to represent forks. | ||
10 | * | ||
11 | * Based on the minix file system code, (C) 1991, 1992 by Linus Torvalds | ||
12 | */ | ||
13 | |||
14 | #include "hfs_fs.h" | ||
15 | #include "btree.h" | ||
16 | |||
17 | /* | ||
18 | * hfs_lookup() | ||
19 | */ | ||
20 | static struct dentry *hfs_lookup(struct inode *dir, struct dentry *dentry, | ||
21 | struct nameidata *nd) | ||
22 | { | ||
23 | hfs_cat_rec rec; | ||
24 | struct hfs_find_data fd; | ||
25 | struct inode *inode = NULL; | ||
26 | int res; | ||
27 | |||
28 | dentry->d_op = &hfs_dentry_operations; | ||
29 | |||
30 | hfs_find_init(HFS_SB(dir->i_sb)->cat_tree, &fd); | ||
31 | hfs_cat_build_key(fd.search_key, dir->i_ino, &dentry->d_name); | ||
32 | res = hfs_brec_read(&fd, &rec, sizeof(rec)); | ||
33 | if (res) { | ||
34 | hfs_find_exit(&fd); | ||
35 | if (res == -ENOENT) { | ||
36 | /* No such entry */ | ||
37 | inode = NULL; | ||
38 | goto done; | ||
39 | } | ||
40 | return ERR_PTR(res); | ||
41 | } | ||
42 | inode = hfs_iget(dir->i_sb, &fd.search_key->cat, &rec); | ||
43 | hfs_find_exit(&fd); | ||
44 | if (!inode) | ||
45 | return ERR_PTR(-EACCES); | ||
46 | done: | ||
47 | d_add(dentry, inode); | ||
48 | return NULL; | ||
49 | } | ||
50 | |||
51 | /* | ||
52 | * hfs_readdir | ||
53 | */ | ||
54 | static int hfs_readdir(struct file *filp, void *dirent, filldir_t filldir) | ||
55 | { | ||
56 | struct inode *inode = filp->f_dentry->d_inode; | ||
57 | struct super_block *sb = inode->i_sb; | ||
58 | int len, err; | ||
59 | char strbuf[HFS_NAMELEN + 1]; | ||
60 | union hfs_cat_rec entry; | ||
61 | struct hfs_find_data fd; | ||
62 | struct hfs_readdir_data *rd; | ||
63 | u16 type; | ||
64 | |||
65 | if (filp->f_pos >= inode->i_size) | ||
66 | return 0; | ||
67 | |||
68 | hfs_find_init(HFS_SB(sb)->cat_tree, &fd); | ||
69 | hfs_cat_build_key(fd.search_key, inode->i_ino, NULL); | ||
70 | err = hfs_brec_find(&fd); | ||
71 | if (err) | ||
72 | goto out; | ||
73 | |||
74 | switch ((u32)filp->f_pos) { | ||
75 | case 0: | ||
76 | /* This is completely artificial... */ | ||
77 | if (filldir(dirent, ".", 1, 0, inode->i_ino, DT_DIR)) | ||
78 | goto out; | ||
79 | filp->f_pos++; | ||
80 | /* fall through */ | ||
81 | case 1: | ||
82 | hfs_bnode_read(fd.bnode, &entry, fd.entryoffset, fd.entrylength); | ||
83 | if (entry.type != HFS_CDR_THD) { | ||
84 | printk("HFS: bad catalog folder thread\n"); | ||
85 | err = -EIO; | ||
86 | goto out; | ||
87 | } | ||
88 | //if (fd.entrylength < HFS_MIN_THREAD_SZ) { | ||
89 | // printk("HFS: truncated catalog thread\n"); | ||
90 | // err = -EIO; | ||
91 | // goto out; | ||
92 | //} | ||
93 | if (filldir(dirent, "..", 2, 1, | ||
94 | be32_to_cpu(entry.thread.ParID), DT_DIR)) | ||
95 | goto out; | ||
96 | filp->f_pos++; | ||
97 | /* fall through */ | ||
98 | default: | ||
99 | if (filp->f_pos >= inode->i_size) | ||
100 | goto out; | ||
101 | err = hfs_brec_goto(&fd, filp->f_pos - 1); | ||
102 | if (err) | ||
103 | goto out; | ||
104 | } | ||
105 | |||
106 | for (;;) { | ||
107 | if (be32_to_cpu(fd.key->cat.ParID) != inode->i_ino) { | ||
108 | printk("HFS: walked past end of dir\n"); | ||
109 | err = -EIO; | ||
110 | goto out; | ||
111 | } | ||
112 | hfs_bnode_read(fd.bnode, &entry, fd.entryoffset, fd.entrylength); | ||
113 | type = entry.type; | ||
114 | len = hfs_mac2triv(strbuf, &fd.key->cat.CName); | ||
115 | if (type == HFS_CDR_DIR) { | ||
116 | if (fd.entrylength < sizeof(struct hfs_cat_dir)) { | ||
117 | printk("HFS: small dir entry\n"); | ||
118 | err = -EIO; | ||
119 | goto out; | ||
120 | } | ||
121 | if (filldir(dirent, strbuf, len, filp->f_pos, | ||
122 | be32_to_cpu(entry.dir.DirID), DT_DIR)) | ||
123 | break; | ||
124 | } else if (type == HFS_CDR_FIL) { | ||
125 | if (fd.entrylength < sizeof(struct hfs_cat_file)) { | ||
126 | printk("HFS: small file entry\n"); | ||
127 | err = -EIO; | ||
128 | goto out; | ||
129 | } | ||
130 | if (filldir(dirent, strbuf, len, filp->f_pos, | ||
131 | be32_to_cpu(entry.file.FlNum), DT_REG)) | ||
132 | break; | ||
133 | } else { | ||
134 | printk("HFS: bad catalog entry type %d\n", type); | ||
135 | err = -EIO; | ||
136 | goto out; | ||
137 | } | ||
138 | filp->f_pos++; | ||
139 | if (filp->f_pos >= inode->i_size) | ||
140 | goto out; | ||
141 | err = hfs_brec_goto(&fd, 1); | ||
142 | if (err) | ||
143 | goto out; | ||
144 | } | ||
145 | rd = filp->private_data; | ||
146 | if (!rd) { | ||
147 | rd = kmalloc(sizeof(struct hfs_readdir_data), GFP_KERNEL); | ||
148 | if (!rd) { | ||
149 | err = -ENOMEM; | ||
150 | goto out; | ||
151 | } | ||
152 | filp->private_data = rd; | ||
153 | rd->file = filp; | ||
154 | list_add(&rd->list, &HFS_I(inode)->open_dir_list); | ||
155 | } | ||
156 | memcpy(&rd->key, &fd.key, sizeof(struct hfs_cat_key)); | ||
157 | out: | ||
158 | hfs_find_exit(&fd); | ||
159 | return err; | ||
160 | } | ||
161 | |||
162 | static int hfs_dir_release(struct inode *inode, struct file *file) | ||
163 | { | ||
164 | struct hfs_readdir_data *rd = file->private_data; | ||
165 | if (rd) { | ||
166 | list_del(&rd->list); | ||
167 | kfree(rd); | ||
168 | } | ||
169 | return 0; | ||
170 | } | ||
171 | |||
172 | /* | ||
173 | * hfs_create() | ||
174 | * | ||
175 | * This is the create() entry in the inode_operations structure for | ||
176 | * regular HFS directories. The purpose is to create a new file in | ||
177 | * a directory and return a corresponding inode, given the inode for | ||
178 | * the directory and the name (and its length) of the new file. | ||
179 | */ | ||
180 | static int hfs_create(struct inode *dir, struct dentry *dentry, int mode, | ||
181 | struct nameidata *nd) | ||
182 | { | ||
183 | struct inode *inode; | ||
184 | int res; | ||
185 | |||
186 | inode = hfs_new_inode(dir, &dentry->d_name, mode); | ||
187 | if (!inode) | ||
188 | return -ENOSPC; | ||
189 | |||
190 | res = hfs_cat_create(inode->i_ino, dir, &dentry->d_name, inode); | ||
191 | if (res) { | ||
192 | inode->i_nlink = 0; | ||
193 | hfs_delete_inode(inode); | ||
194 | iput(inode); | ||
195 | return res; | ||
196 | } | ||
197 | d_instantiate(dentry, inode); | ||
198 | mark_inode_dirty(inode); | ||
199 | return 0; | ||
200 | } | ||
201 | |||
202 | /* | ||
203 | * hfs_mkdir() | ||
204 | * | ||
205 | * This is the mkdir() entry in the inode_operations structure for | ||
206 | * regular HFS directories. The purpose is to create a new directory | ||
207 | * in a directory, given the inode for the parent directory and the | ||
208 | * name (and its length) of the new directory. | ||
209 | */ | ||
210 | static int hfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) | ||
211 | { | ||
212 | struct inode *inode; | ||
213 | int res; | ||
214 | |||
215 | inode = hfs_new_inode(dir, &dentry->d_name, S_IFDIR | mode); | ||
216 | if (!inode) | ||
217 | return -ENOSPC; | ||
218 | |||
219 | res = hfs_cat_create(inode->i_ino, dir, &dentry->d_name, inode); | ||
220 | if (res) { | ||
221 | inode->i_nlink = 0; | ||
222 | hfs_delete_inode(inode); | ||
223 | iput(inode); | ||
224 | return res; | ||
225 | } | ||
226 | d_instantiate(dentry, inode); | ||
227 | mark_inode_dirty(inode); | ||
228 | return 0; | ||
229 | } | ||
230 | |||
231 | /* | ||
232 | * hfs_unlink() | ||
233 | * | ||
234 | * This is the unlink() entry in the inode_operations structure for | ||
235 | * regular HFS directories. The purpose is to delete an existing | ||
236 | * file, given the inode for the parent directory and the name | ||
237 | * (and its length) of the existing file. | ||
238 | */ | ||
239 | static int hfs_unlink(struct inode *dir, struct dentry *dentry) | ||
240 | { | ||
241 | struct inode *inode; | ||
242 | int res; | ||
243 | |||
244 | inode = dentry->d_inode; | ||
245 | res = hfs_cat_delete(inode->i_ino, dir, &dentry->d_name); | ||
246 | if (res) | ||
247 | return res; | ||
248 | |||
249 | inode->i_nlink--; | ||
250 | hfs_delete_inode(inode); | ||
251 | inode->i_ctime = CURRENT_TIME_SEC; | ||
252 | mark_inode_dirty(inode); | ||
253 | |||
254 | return res; | ||
255 | } | ||
256 | |||
257 | /* | ||
258 | * hfs_rmdir() | ||
259 | * | ||
260 | * This is the rmdir() entry in the inode_operations structure for | ||
261 | * regular HFS directories. The purpose is to delete an existing | ||
262 | * directory, given the inode for the parent directory and the name | ||
263 | * (and its length) of the existing directory. | ||
264 | */ | ||
265 | static int hfs_rmdir(struct inode *dir, struct dentry *dentry) | ||
266 | { | ||
267 | struct inode *inode; | ||
268 | int res; | ||
269 | |||
270 | inode = dentry->d_inode; | ||
271 | if (inode->i_size != 2) | ||
272 | return -ENOTEMPTY; | ||
273 | res = hfs_cat_delete(inode->i_ino, dir, &dentry->d_name); | ||
274 | if (res) | ||
275 | return res; | ||
276 | inode->i_nlink = 0; | ||
277 | inode->i_ctime = CURRENT_TIME_SEC; | ||
278 | hfs_delete_inode(inode); | ||
279 | mark_inode_dirty(inode); | ||
280 | return 0; | ||
281 | } | ||
282 | |||
283 | /* | ||
284 | * hfs_rename() | ||
285 | * | ||
286 | * This is the rename() entry in the inode_operations structure for | ||
287 | * regular HFS directories. The purpose is to rename an existing | ||
288 | * file or directory, given the inode for the current directory and | ||
289 | * the name (and its length) of the existing file/directory and the | ||
290 | * inode for the new directory and the name (and its length) of the | ||
291 | * new file/directory. | ||
292 | * XXX: how do you handle must_be dir? | ||
293 | */ | ||
294 | static int hfs_rename(struct inode *old_dir, struct dentry *old_dentry, | ||
295 | struct inode *new_dir, struct dentry *new_dentry) | ||
296 | { | ||
297 | int res; | ||
298 | |||
299 | /* Unlink destination if it already exists */ | ||
300 | if (new_dentry->d_inode) { | ||
301 | res = hfs_unlink(new_dir, new_dentry); | ||
302 | if (res) | ||
303 | return res; | ||
304 | } | ||
305 | |||
306 | res = hfs_cat_move(old_dentry->d_inode->i_ino, | ||
307 | old_dir, &old_dentry->d_name, | ||
308 | new_dir, &new_dentry->d_name); | ||
309 | if (!res) | ||
310 | hfs_cat_build_key((btree_key *)&HFS_I(old_dentry->d_inode)->cat_key, | ||
311 | new_dir->i_ino, &new_dentry->d_name); | ||
312 | return res; | ||
313 | } | ||
314 | |||
315 | struct file_operations hfs_dir_operations = { | ||
316 | .read = generic_read_dir, | ||
317 | .readdir = hfs_readdir, | ||
318 | .llseek = generic_file_llseek, | ||
319 | .release = hfs_dir_release, | ||
320 | }; | ||
321 | |||
322 | struct inode_operations hfs_dir_inode_operations = { | ||
323 | .create = hfs_create, | ||
324 | .lookup = hfs_lookup, | ||
325 | .unlink = hfs_unlink, | ||
326 | .mkdir = hfs_mkdir, | ||
327 | .rmdir = hfs_rmdir, | ||
328 | .rename = hfs_rename, | ||
329 | .setattr = hfs_inode_setattr, | ||
330 | }; | ||
diff --git a/fs/hfs/extent.c b/fs/hfs/extent.c new file mode 100644 index 000000000000..cbc8510ad222 --- /dev/null +++ b/fs/hfs/extent.c | |||
@@ -0,0 +1,527 @@ | |||
1 | /* | ||
2 | * linux/fs/hfs/extent.c | ||
3 | * | ||
4 | * Copyright (C) 1995-1997 Paul H. Hargrove | ||
5 | * (C) 2003 Ardis Technologies <roman@ardistech.com> | ||
6 | * This file may be distributed under the terms of the GNU General Public License. | ||
7 | * | ||
8 | * This file contains the functions related to the extents B-tree. | ||
9 | */ | ||
10 | |||
11 | #include <linux/pagemap.h> | ||
12 | |||
13 | #include "hfs_fs.h" | ||
14 | #include "btree.h" | ||
15 | |||
16 | /*================ File-local functions ================*/ | ||
17 | |||
18 | /* | ||
19 | * build_key | ||
20 | */ | ||
21 | static void hfs_ext_build_key(hfs_btree_key *key, u32 cnid, u16 block, u8 type) | ||
22 | { | ||
23 | key->key_len = 7; | ||
24 | key->ext.FkType = type; | ||
25 | key->ext.FNum = cpu_to_be32(cnid); | ||
26 | key->ext.FABN = cpu_to_be16(block); | ||
27 | } | ||
28 | |||
29 | /* | ||
30 | * hfs_ext_compare() | ||
31 | * | ||
32 | * Description: | ||
33 | * This is the comparison function used for the extents B-tree. In | ||
34 | * comparing extent B-tree entries, the file id is the most | ||
35 | * significant field (compared as unsigned ints); the fork type is | ||
36 | * the second most significant field (compared as unsigned chars); | ||
37 | * and the allocation block number field is the least significant | ||
38 | * (compared as unsigned ints). | ||
39 | * Input Variable(s): | ||
40 | * struct hfs_ext_key *key1: pointer to the first key to compare | ||
41 | * struct hfs_ext_key *key2: pointer to the second key to compare | ||
42 | * Output Variable(s): | ||
43 | * NONE | ||
44 | * Returns: | ||
45 | * int: negative if key1<key2, positive if key1>key2, and 0 if key1==key2 | ||
46 | * Preconditions: | ||
47 | * key1 and key2 point to "valid" (struct hfs_ext_key)s. | ||
48 | * Postconditions: | ||
49 | * This function has no side-effects */ | ||
50 | int hfs_ext_keycmp(const btree_key *key1, const btree_key *key2) | ||
51 | { | ||
52 | __be32 fnum1, fnum2; | ||
53 | __be16 block1, block2; | ||
54 | |||
55 | fnum1 = key1->ext.FNum; | ||
56 | fnum2 = key2->ext.FNum; | ||
57 | if (fnum1 != fnum2) | ||
58 | return be32_to_cpu(fnum1) < be32_to_cpu(fnum2) ? -1 : 1; | ||
59 | if (key1->ext.FkType != key2->ext.FkType) | ||
60 | return key1->ext.FkType < key2->ext.FkType ? -1 : 1; | ||
61 | |||
62 | block1 = key1->ext.FABN; | ||
63 | block2 = key2->ext.FABN; | ||
64 | if (block1 == block2) | ||
65 | return 0; | ||
66 | return be16_to_cpu(block1) < be16_to_cpu(block2) ? -1 : 1; | ||
67 | } | ||
68 | |||
69 | /* | ||
70 | * hfs_ext_find_block | ||
71 | * | ||
72 | * Find a block within an extent record | ||
73 | */ | ||
74 | static u16 hfs_ext_find_block(struct hfs_extent *ext, u16 off) | ||
75 | { | ||
76 | int i; | ||
77 | u16 count; | ||
78 | |||
79 | for (i = 0; i < 3; ext++, i++) { | ||
80 | count = be16_to_cpu(ext->count); | ||
81 | if (off < count) | ||
82 | return be16_to_cpu(ext->block) + off; | ||
83 | off -= count; | ||
84 | } | ||
85 | /* panic? */ | ||
86 | return 0; | ||
87 | } | ||
88 | |||
89 | static int hfs_ext_block_count(struct hfs_extent *ext) | ||
90 | { | ||
91 | int i; | ||
92 | u16 count = 0; | ||
93 | |||
94 | for (i = 0; i < 3; ext++, i++) | ||
95 | count += be16_to_cpu(ext->count); | ||
96 | return count; | ||
97 | } | ||
98 | |||
99 | static u16 hfs_ext_lastblock(struct hfs_extent *ext) | ||
100 | { | ||
101 | int i; | ||
102 | |||
103 | ext += 2; | ||
104 | for (i = 0; i < 2; ext--, i++) | ||
105 | if (ext->count) | ||
106 | break; | ||
107 | return be16_to_cpu(ext->block) + be16_to_cpu(ext->count); | ||
108 | } | ||
109 | |||
110 | static void __hfs_ext_write_extent(struct inode *inode, struct hfs_find_data *fd) | ||
111 | { | ||
112 | int res; | ||
113 | |||
114 | hfs_ext_build_key(fd->search_key, inode->i_ino, HFS_I(inode)->cached_start, | ||
115 | HFS_IS_RSRC(inode) ? HFS_FK_RSRC : HFS_FK_DATA); | ||
116 | res = hfs_brec_find(fd); | ||
117 | if (HFS_I(inode)->flags & HFS_FLG_EXT_NEW) { | ||
118 | if (res != -ENOENT) | ||
119 | return; | ||
120 | hfs_brec_insert(fd, HFS_I(inode)->cached_extents, sizeof(hfs_extent_rec)); | ||
121 | HFS_I(inode)->flags &= ~(HFS_FLG_EXT_DIRTY|HFS_FLG_EXT_NEW); | ||
122 | } else { | ||
123 | if (res) | ||
124 | return; | ||
125 | hfs_bnode_write(fd->bnode, HFS_I(inode)->cached_extents, fd->entryoffset, fd->entrylength); | ||
126 | HFS_I(inode)->flags &= ~HFS_FLG_EXT_DIRTY; | ||
127 | } | ||
128 | } | ||
129 | |||
130 | void hfs_ext_write_extent(struct inode *inode) | ||
131 | { | ||
132 | struct hfs_find_data fd; | ||
133 | |||
134 | if (HFS_I(inode)->flags & HFS_FLG_EXT_DIRTY) { | ||
135 | hfs_find_init(HFS_SB(inode->i_sb)->ext_tree, &fd); | ||
136 | __hfs_ext_write_extent(inode, &fd); | ||
137 | hfs_find_exit(&fd); | ||
138 | } | ||
139 | } | ||
140 | |||
141 | static inline int __hfs_ext_read_extent(struct hfs_find_data *fd, struct hfs_extent *extent, | ||
142 | u32 cnid, u32 block, u8 type) | ||
143 | { | ||
144 | int res; | ||
145 | |||
146 | hfs_ext_build_key(fd->search_key, cnid, block, type); | ||
147 | fd->key->ext.FNum = 0; | ||
148 | res = hfs_brec_find(fd); | ||
149 | if (res && res != -ENOENT) | ||
150 | return res; | ||
151 | if (fd->key->ext.FNum != fd->search_key->ext.FNum || | ||
152 | fd->key->ext.FkType != fd->search_key->ext.FkType) | ||
153 | return -ENOENT; | ||
154 | if (fd->entrylength != sizeof(hfs_extent_rec)) | ||
155 | return -EIO; | ||
156 | hfs_bnode_read(fd->bnode, extent, fd->entryoffset, sizeof(hfs_extent_rec)); | ||
157 | return 0; | ||
158 | } | ||
159 | |||
160 | static inline int __hfs_ext_cache_extent(struct hfs_find_data *fd, struct inode *inode, u32 block) | ||
161 | { | ||
162 | int res; | ||
163 | |||
164 | if (HFS_I(inode)->flags & HFS_FLG_EXT_DIRTY) | ||
165 | __hfs_ext_write_extent(inode, fd); | ||
166 | |||
167 | res = __hfs_ext_read_extent(fd, HFS_I(inode)->cached_extents, inode->i_ino, | ||
168 | block, HFS_IS_RSRC(inode) ? HFS_FK_RSRC : HFS_FK_DATA); | ||
169 | if (!res) { | ||
170 | HFS_I(inode)->cached_start = be16_to_cpu(fd->key->ext.FABN); | ||
171 | HFS_I(inode)->cached_blocks = hfs_ext_block_count(HFS_I(inode)->cached_extents); | ||
172 | } else { | ||
173 | HFS_I(inode)->cached_start = HFS_I(inode)->cached_blocks = 0; | ||
174 | HFS_I(inode)->flags &= ~(HFS_FLG_EXT_DIRTY|HFS_FLG_EXT_NEW); | ||
175 | } | ||
176 | return res; | ||
177 | } | ||
178 | |||
179 | static int hfs_ext_read_extent(struct inode *inode, u16 block) | ||
180 | { | ||
181 | struct hfs_find_data fd; | ||
182 | int res; | ||
183 | |||
184 | if (block >= HFS_I(inode)->cached_start && | ||
185 | block < HFS_I(inode)->cached_start + HFS_I(inode)->cached_blocks) | ||
186 | return 0; | ||
187 | |||
188 | hfs_find_init(HFS_SB(inode->i_sb)->ext_tree, &fd); | ||
189 | res = __hfs_ext_cache_extent(&fd, inode, block); | ||
190 | hfs_find_exit(&fd); | ||
191 | return res; | ||
192 | } | ||
193 | |||
194 | static void hfs_dump_extent(struct hfs_extent *extent) | ||
195 | { | ||
196 | int i; | ||
197 | |||
198 | dprint(DBG_EXTENT, " "); | ||
199 | for (i = 0; i < 3; i++) | ||
200 | dprint(DBG_EXTENT, " %u:%u", be16_to_cpu(extent[i].block), | ||
201 | be16_to_cpu(extent[i].count)); | ||
202 | dprint(DBG_EXTENT, "\n"); | ||
203 | } | ||
204 | |||
205 | static int hfs_add_extent(struct hfs_extent *extent, u16 offset, | ||
206 | u16 alloc_block, u16 block_count) | ||
207 | { | ||
208 | u16 count, start; | ||
209 | int i; | ||
210 | |||
211 | hfs_dump_extent(extent); | ||
212 | for (i = 0; i < 3; extent++, i++) { | ||
213 | count = be16_to_cpu(extent->count); | ||
214 | if (offset == count) { | ||
215 | start = be16_to_cpu(extent->block); | ||
216 | if (alloc_block != start + count) { | ||
217 | if (++i >= 3) | ||
218 | return -ENOSPC; | ||
219 | extent++; | ||
220 | extent->block = cpu_to_be16(alloc_block); | ||
221 | } else | ||
222 | block_count += count; | ||
223 | extent->count = cpu_to_be16(block_count); | ||
224 | return 0; | ||
225 | } else if (offset < count) | ||
226 | break; | ||
227 | offset -= count; | ||
228 | } | ||
229 | /* panic? */ | ||
230 | return -EIO; | ||
231 | } | ||
232 | |||
233 | static int hfs_free_extents(struct super_block *sb, struct hfs_extent *extent, | ||
234 | u16 offset, u16 block_nr) | ||
235 | { | ||
236 | u16 count, start; | ||
237 | int i; | ||
238 | |||
239 | hfs_dump_extent(extent); | ||
240 | for (i = 0; i < 3; extent++, i++) { | ||
241 | count = be16_to_cpu(extent->count); | ||
242 | if (offset == count) | ||
243 | goto found; | ||
244 | else if (offset < count) | ||
245 | break; | ||
246 | offset -= count; | ||
247 | } | ||
248 | /* panic? */ | ||
249 | return -EIO; | ||
250 | found: | ||
251 | for (;;) { | ||
252 | start = be16_to_cpu(extent->block); | ||
253 | if (count <= block_nr) { | ||
254 | hfs_clear_vbm_bits(sb, start, count); | ||
255 | extent->block = 0; | ||
256 | extent->count = 0; | ||
257 | block_nr -= count; | ||
258 | } else { | ||
259 | count -= block_nr; | ||
260 | hfs_clear_vbm_bits(sb, start + count, block_nr); | ||
261 | extent->count = cpu_to_be16(count); | ||
262 | block_nr = 0; | ||
263 | } | ||
264 | if (!block_nr || !i) | ||
265 | return 0; | ||
266 | i--; | ||
267 | extent--; | ||
268 | count = be16_to_cpu(extent->count); | ||
269 | } | ||
270 | } | ||
271 | |||
272 | int hfs_free_fork(struct super_block *sb, struct hfs_cat_file *file, int type) | ||
273 | { | ||
274 | struct hfs_find_data fd; | ||
275 | u32 total_blocks, blocks, start; | ||
276 | u32 cnid = be32_to_cpu(file->FlNum); | ||
277 | struct hfs_extent *extent; | ||
278 | int res, i; | ||
279 | |||
280 | if (type == HFS_FK_DATA) { | ||
281 | total_blocks = be32_to_cpu(file->PyLen); | ||
282 | extent = file->ExtRec; | ||
283 | } else { | ||
284 | total_blocks = be32_to_cpu(file->RPyLen); | ||
285 | extent = file->RExtRec; | ||
286 | } | ||
287 | total_blocks /= HFS_SB(sb)->alloc_blksz; | ||
288 | if (!total_blocks) | ||
289 | return 0; | ||
290 | |||
291 | blocks = 0; | ||
292 | for (i = 0; i < 3; extent++, i++) | ||
293 | blocks += be16_to_cpu(extent[i].count); | ||
294 | |||
295 | res = hfs_free_extents(sb, extent, blocks, blocks); | ||
296 | if (res) | ||
297 | return res; | ||
298 | if (total_blocks == blocks) | ||
299 | return 0; | ||
300 | |||
301 | hfs_find_init(HFS_SB(sb)->ext_tree, &fd); | ||
302 | do { | ||
303 | res = __hfs_ext_read_extent(&fd, extent, cnid, total_blocks, type); | ||
304 | if (res) | ||
305 | break; | ||
306 | start = be16_to_cpu(fd.key->ext.FABN); | ||
307 | hfs_free_extents(sb, extent, total_blocks - start, total_blocks); | ||
308 | hfs_brec_remove(&fd); | ||
309 | total_blocks = start; | ||
310 | } while (total_blocks > blocks); | ||
311 | hfs_find_exit(&fd); | ||
312 | |||
313 | return res; | ||
314 | } | ||
315 | |||
316 | /* | ||
317 | * hfs_get_block | ||
318 | */ | ||
319 | int hfs_get_block(struct inode *inode, sector_t block, | ||
320 | struct buffer_head *bh_result, int create) | ||
321 | { | ||
322 | struct super_block *sb; | ||
323 | u16 dblock, ablock; | ||
324 | int res; | ||
325 | |||
326 | sb = inode->i_sb; | ||
327 | /* Convert inode block to disk allocation block */ | ||
328 | ablock = (u32)block / HFS_SB(sb)->fs_div; | ||
329 | |||
330 | if (block >= HFS_I(inode)->fs_blocks) { | ||
331 | if (block > HFS_I(inode)->fs_blocks || !create) | ||
332 | return -EIO; | ||
333 | if (ablock >= HFS_I(inode)->alloc_blocks) { | ||
334 | res = hfs_extend_file(inode); | ||
335 | if (res) | ||
336 | return res; | ||
337 | } | ||
338 | } else | ||
339 | create = 0; | ||
340 | |||
341 | if (ablock < HFS_I(inode)->first_blocks) { | ||
342 | dblock = hfs_ext_find_block(HFS_I(inode)->first_extents, ablock); | ||
343 | goto done; | ||
344 | } | ||
345 | |||
346 | down(&HFS_I(inode)->extents_lock); | ||
347 | res = hfs_ext_read_extent(inode, ablock); | ||
348 | if (!res) | ||
349 | dblock = hfs_ext_find_block(HFS_I(inode)->cached_extents, | ||
350 | ablock - HFS_I(inode)->cached_start); | ||
351 | else { | ||
352 | up(&HFS_I(inode)->extents_lock); | ||
353 | return -EIO; | ||
354 | } | ||
355 | up(&HFS_I(inode)->extents_lock); | ||
356 | |||
357 | done: | ||
358 | map_bh(bh_result, sb, HFS_SB(sb)->fs_start + | ||
359 | dblock * HFS_SB(sb)->fs_div + | ||
360 | (u32)block % HFS_SB(sb)->fs_div); | ||
361 | |||
362 | if (create) { | ||
363 | set_buffer_new(bh_result); | ||
364 | HFS_I(inode)->phys_size += sb->s_blocksize; | ||
365 | HFS_I(inode)->fs_blocks++; | ||
366 | inode_add_bytes(inode, sb->s_blocksize); | ||
367 | mark_inode_dirty(inode); | ||
368 | } | ||
369 | return 0; | ||
370 | } | ||
371 | |||
372 | int hfs_extend_file(struct inode *inode) | ||
373 | { | ||
374 | struct super_block *sb = inode->i_sb; | ||
375 | u32 start, len, goal; | ||
376 | int res; | ||
377 | |||
378 | down(&HFS_I(inode)->extents_lock); | ||
379 | if (HFS_I(inode)->alloc_blocks == HFS_I(inode)->first_blocks) | ||
380 | goal = hfs_ext_lastblock(HFS_I(inode)->first_extents); | ||
381 | else { | ||
382 | res = hfs_ext_read_extent(inode, HFS_I(inode)->alloc_blocks); | ||
383 | if (res) | ||
384 | goto out; | ||
385 | goal = hfs_ext_lastblock(HFS_I(inode)->cached_extents); | ||
386 | } | ||
387 | |||
388 | len = HFS_I(inode)->clump_blocks; | ||
389 | start = hfs_vbm_search_free(sb, goal, &len); | ||
390 | if (!len) { | ||
391 | res = -ENOSPC; | ||
392 | goto out; | ||
393 | } | ||
394 | |||
395 | dprint(DBG_EXTENT, "extend %lu: %u,%u\n", inode->i_ino, start, len); | ||
396 | if (HFS_I(inode)->alloc_blocks == HFS_I(inode)->first_blocks) { | ||
397 | if (!HFS_I(inode)->first_blocks) { | ||
398 | dprint(DBG_EXTENT, "first extents\n"); | ||
399 | /* no extents yet */ | ||
400 | HFS_I(inode)->first_extents[0].block = cpu_to_be16(start); | ||
401 | HFS_I(inode)->first_extents[0].count = cpu_to_be16(len); | ||
402 | res = 0; | ||
403 | } else { | ||
404 | /* try to append to extents in inode */ | ||
405 | res = hfs_add_extent(HFS_I(inode)->first_extents, | ||
406 | HFS_I(inode)->alloc_blocks, | ||
407 | start, len); | ||
408 | if (res == -ENOSPC) | ||
409 | goto insert_extent; | ||
410 | } | ||
411 | if (!res) { | ||
412 | hfs_dump_extent(HFS_I(inode)->first_extents); | ||
413 | HFS_I(inode)->first_blocks += len; | ||
414 | } | ||
415 | } else { | ||
416 | res = hfs_add_extent(HFS_I(inode)->cached_extents, | ||
417 | HFS_I(inode)->alloc_blocks - | ||
418 | HFS_I(inode)->cached_start, | ||
419 | start, len); | ||
420 | if (!res) { | ||
421 | hfs_dump_extent(HFS_I(inode)->cached_extents); | ||
422 | HFS_I(inode)->flags |= HFS_FLG_EXT_DIRTY; | ||
423 | HFS_I(inode)->cached_blocks += len; | ||
424 | } else if (res == -ENOSPC) | ||
425 | goto insert_extent; | ||
426 | } | ||
427 | out: | ||
428 | up(&HFS_I(inode)->extents_lock); | ||
429 | if (!res) { | ||
430 | HFS_I(inode)->alloc_blocks += len; | ||
431 | mark_inode_dirty(inode); | ||
432 | if (inode->i_ino < HFS_FIRSTUSER_CNID) | ||
433 | set_bit(HFS_FLG_ALT_MDB_DIRTY, &HFS_SB(sb)->flags); | ||
434 | set_bit(HFS_FLG_MDB_DIRTY, &HFS_SB(sb)->flags); | ||
435 | sb->s_dirt = 1; | ||
436 | } | ||
437 | return res; | ||
438 | |||
439 | insert_extent: | ||
440 | dprint(DBG_EXTENT, "insert new extent\n"); | ||
441 | hfs_ext_write_extent(inode); | ||
442 | |||
443 | memset(HFS_I(inode)->cached_extents, 0, sizeof(hfs_extent_rec)); | ||
444 | HFS_I(inode)->cached_extents[0].block = cpu_to_be16(start); | ||
445 | HFS_I(inode)->cached_extents[0].count = cpu_to_be16(len); | ||
446 | hfs_dump_extent(HFS_I(inode)->cached_extents); | ||
447 | HFS_I(inode)->flags |= HFS_FLG_EXT_DIRTY|HFS_FLG_EXT_NEW; | ||
448 | HFS_I(inode)->cached_start = HFS_I(inode)->alloc_blocks; | ||
449 | HFS_I(inode)->cached_blocks = len; | ||
450 | |||
451 | res = 0; | ||
452 | goto out; | ||
453 | } | ||
454 | |||
455 | void hfs_file_truncate(struct inode *inode) | ||
456 | { | ||
457 | struct super_block *sb = inode->i_sb; | ||
458 | struct hfs_find_data fd; | ||
459 | u16 blk_cnt, alloc_cnt, start; | ||
460 | u32 size; | ||
461 | int res; | ||
462 | |||
463 | dprint(DBG_INODE, "truncate: %lu, %Lu -> %Lu\n", inode->i_ino, | ||
464 | (long long)HFS_I(inode)->phys_size, inode->i_size); | ||
465 | if (inode->i_size > HFS_I(inode)->phys_size) { | ||
466 | struct address_space *mapping = inode->i_mapping; | ||
467 | struct page *page; | ||
468 | int res; | ||
469 | |||
470 | size = inode->i_size - 1; | ||
471 | page = grab_cache_page(mapping, size >> PAGE_CACHE_SHIFT); | ||
472 | if (!page) | ||
473 | return; | ||
474 | size &= PAGE_CACHE_SIZE - 1; | ||
475 | size++; | ||
476 | res = mapping->a_ops->prepare_write(NULL, page, size, size); | ||
477 | if (!res) | ||
478 | res = mapping->a_ops->commit_write(NULL, page, size, size); | ||
479 | if (res) | ||
480 | inode->i_size = HFS_I(inode)->phys_size; | ||
481 | unlock_page(page); | ||
482 | page_cache_release(page); | ||
483 | mark_inode_dirty(inode); | ||
484 | return; | ||
485 | } | ||
486 | size = inode->i_size + HFS_SB(sb)->alloc_blksz - 1; | ||
487 | blk_cnt = size / HFS_SB(sb)->alloc_blksz; | ||
488 | alloc_cnt = HFS_I(inode)->alloc_blocks; | ||
489 | if (blk_cnt == alloc_cnt) | ||
490 | goto out; | ||
491 | |||
492 | down(&HFS_I(inode)->extents_lock); | ||
493 | hfs_find_init(HFS_SB(sb)->ext_tree, &fd); | ||
494 | while (1) { | ||
495 | if (alloc_cnt == HFS_I(inode)->first_blocks) { | ||
496 | hfs_free_extents(sb, HFS_I(inode)->first_extents, | ||
497 | alloc_cnt, alloc_cnt - blk_cnt); | ||
498 | hfs_dump_extent(HFS_I(inode)->first_extents); | ||
499 | HFS_I(inode)->first_blocks = blk_cnt; | ||
500 | break; | ||
501 | } | ||
502 | res = __hfs_ext_cache_extent(&fd, inode, alloc_cnt); | ||
503 | if (res) | ||
504 | break; | ||
505 | start = HFS_I(inode)->cached_start; | ||
506 | hfs_free_extents(sb, HFS_I(inode)->cached_extents, | ||
507 | alloc_cnt - start, alloc_cnt - blk_cnt); | ||
508 | hfs_dump_extent(HFS_I(inode)->cached_extents); | ||
509 | if (blk_cnt > start) { | ||
510 | HFS_I(inode)->flags |= HFS_FLG_EXT_DIRTY; | ||
511 | break; | ||
512 | } | ||
513 | alloc_cnt = start; | ||
514 | HFS_I(inode)->cached_start = HFS_I(inode)->cached_blocks = 0; | ||
515 | HFS_I(inode)->flags &= ~(HFS_FLG_EXT_DIRTY|HFS_FLG_EXT_NEW); | ||
516 | hfs_brec_remove(&fd); | ||
517 | } | ||
518 | hfs_find_exit(&fd); | ||
519 | up(&HFS_I(inode)->extents_lock); | ||
520 | |||
521 | HFS_I(inode)->alloc_blocks = blk_cnt; | ||
522 | out: | ||
523 | HFS_I(inode)->phys_size = inode->i_size; | ||
524 | HFS_I(inode)->fs_blocks = (inode->i_size + sb->s_blocksize - 1) >> sb->s_blocksize_bits; | ||
525 | inode_set_bytes(inode, HFS_I(inode)->fs_blocks << sb->s_blocksize_bits); | ||
526 | mark_inode_dirty(inode); | ||
527 | } | ||
diff --git a/fs/hfs/hfs.h b/fs/hfs/hfs.h new file mode 100644 index 000000000000..df6b33adee3b --- /dev/null +++ b/fs/hfs/hfs.h | |||
@@ -0,0 +1,287 @@ | |||
1 | /* | ||
2 | * linux/fs/hfs/hfs.h | ||
3 | * | ||
4 | * Copyright (C) 1995-1997 Paul H. Hargrove | ||
5 | * (C) 2003 Ardis Technologies <roman@ardistech.com> | ||
6 | * This file may be distributed under the terms of the GNU General Public License. | ||
7 | */ | ||
8 | |||
9 | #ifndef _HFS_H | ||
10 | #define _HFS_H | ||
11 | |||
12 | /* offsets to various blocks */ | ||
13 | #define HFS_DD_BLK 0 /* Driver Descriptor block */ | ||
14 | #define HFS_PMAP_BLK 1 /* First block of partition map */ | ||
15 | #define HFS_MDB_BLK 2 /* Block (w/i partition) of MDB */ | ||
16 | |||
17 | /* magic numbers for various disk blocks */ | ||
18 | #define HFS_DRVR_DESC_MAGIC 0x4552 /* "ER": driver descriptor map */ | ||
19 | #define HFS_OLD_PMAP_MAGIC 0x5453 /* "TS": old-type partition map */ | ||
20 | #define HFS_NEW_PMAP_MAGIC 0x504D /* "PM": new-type partition map */ | ||
21 | #define HFS_SUPER_MAGIC 0x4244 /* "BD": HFS MDB (super block) */ | ||
22 | #define HFS_MFS_SUPER_MAGIC 0xD2D7 /* MFS MDB (super block) */ | ||
23 | |||
24 | /* various FIXED size parameters */ | ||
25 | #define HFS_SECTOR_SIZE 512 /* size of an HFS sector */ | ||
26 | #define HFS_SECTOR_SIZE_BITS 9 /* log_2(HFS_SECTOR_SIZE) */ | ||
27 | #define HFS_NAMELEN 31 /* maximum length of an HFS filename */ | ||
28 | #define HFS_MAX_VALENCE 32767U | ||
29 | |||
30 | /* Meanings of the drAtrb field of the MDB, | ||
31 | * Reference: _Inside Macintosh: Files_ p. 2-61 | ||
32 | */ | ||
33 | #define HFS_SB_ATTRIB_HLOCK (1 << 7) | ||
34 | #define HFS_SB_ATTRIB_UNMNT (1 << 8) | ||
35 | #define HFS_SB_ATTRIB_SPARED (1 << 9) | ||
36 | #define HFS_SB_ATTRIB_INCNSTNT (1 << 11) | ||
37 | #define HFS_SB_ATTRIB_SLOCK (1 << 15) | ||
38 | |||
39 | /* Some special File ID numbers */ | ||
40 | #define HFS_POR_CNID 1 /* Parent Of the Root */ | ||
41 | #define HFS_ROOT_CNID 2 /* ROOT directory */ | ||
42 | #define HFS_EXT_CNID 3 /* EXTents B-tree */ | ||
43 | #define HFS_CAT_CNID 4 /* CATalog B-tree */ | ||
44 | #define HFS_BAD_CNID 5 /* BAD blocks file */ | ||
45 | #define HFS_ALLOC_CNID 6 /* ALLOCation file (HFS+) */ | ||
46 | #define HFS_START_CNID 7 /* STARTup file (HFS+) */ | ||
47 | #define HFS_ATTR_CNID 8 /* ATTRibutes file (HFS+) */ | ||
48 | #define HFS_EXCH_CNID 15 /* ExchangeFiles temp id */ | ||
49 | #define HFS_FIRSTUSER_CNID 16 | ||
50 | |||
51 | /* values for hfs_cat_rec.cdrType */ | ||
52 | #define HFS_CDR_DIR 0x01 /* folder (directory) */ | ||
53 | #define HFS_CDR_FIL 0x02 /* file */ | ||
54 | #define HFS_CDR_THD 0x03 /* folder (directory) thread */ | ||
55 | #define HFS_CDR_FTH 0x04 /* file thread */ | ||
56 | |||
57 | /* legal values for hfs_ext_key.FkType and hfs_file.fork */ | ||
58 | #define HFS_FK_DATA 0x00 | ||
59 | #define HFS_FK_RSRC 0xFF | ||
60 | |||
61 | /* bits in hfs_fil_entry.Flags */ | ||
62 | #define HFS_FIL_LOCK 0x01 /* locked */ | ||
63 | #define HFS_FIL_THD 0x02 /* file thread */ | ||
64 | #define HFS_FIL_DOPEN 0x04 /* data fork open */ | ||
65 | #define HFS_FIL_ROPEN 0x08 /* resource fork open */ | ||
66 | #define HFS_FIL_DIR 0x10 /* directory (always clear) */ | ||
67 | #define HFS_FIL_NOCOPY 0x40 /* copy-protected file */ | ||
68 | #define HFS_FIL_USED 0x80 /* open */ | ||
69 | |||
70 | /* bits in hfs_dir_entry.Flags. dirflags is 16 bits. */ | ||
71 | #define HFS_DIR_LOCK 0x01 /* locked */ | ||
72 | #define HFS_DIR_THD 0x02 /* directory thread */ | ||
73 | #define HFS_DIR_INEXPFOLDER 0x04 /* in a shared area */ | ||
74 | #define HFS_DIR_MOUNTED 0x08 /* mounted */ | ||
75 | #define HFS_DIR_DIR 0x10 /* directory (always set) */ | ||
76 | #define HFS_DIR_EXPFOLDER 0x20 /* share point */ | ||
77 | |||
78 | /* bits hfs_finfo.fdFlags */ | ||
79 | #define HFS_FLG_INITED 0x0100 | ||
80 | #define HFS_FLG_LOCKED 0x1000 | ||
81 | #define HFS_FLG_INVISIBLE 0x4000 | ||
82 | |||
83 | /*======== HFS structures as they appear on the disk ========*/ | ||
84 | |||
85 | #define __packed __attribute__ ((packed)) | ||
86 | |||
87 | /* Pascal-style string of up to 31 characters */ | ||
88 | struct hfs_name { | ||
89 | u8 len; | ||
90 | u8 name[HFS_NAMELEN]; | ||
91 | } __packed; | ||
92 | |||
93 | struct hfs_point { | ||
94 | __be16 v; | ||
95 | __be16 h; | ||
96 | } __packed; | ||
97 | |||
98 | struct hfs_rect { | ||
99 | __be16 top; | ||
100 | __be16 left; | ||
101 | __be16 bottom; | ||
102 | __be16 right; | ||
103 | } __packed; | ||
104 | |||
105 | struct hfs_finfo { | ||
106 | __be32 fdType; | ||
107 | __be32 fdCreator; | ||
108 | __be16 fdFlags; | ||
109 | struct hfs_point fdLocation; | ||
110 | __be16 fdFldr; | ||
111 | } __packed; | ||
112 | |||
113 | struct hfs_fxinfo { | ||
114 | __be16 fdIconID; | ||
115 | u8 fdUnused[8]; | ||
116 | __be16 fdComment; | ||
117 | __be32 fdPutAway; | ||
118 | } __packed; | ||
119 | |||
120 | struct hfs_dinfo { | ||
121 | struct hfs_rect frRect; | ||
122 | __be16 frFlags; | ||
123 | struct hfs_point frLocation; | ||
124 | __be16 frView; | ||
125 | } __packed; | ||
126 | |||
127 | struct hfs_dxinfo { | ||
128 | struct hfs_point frScroll; | ||
129 | __be32 frOpenChain; | ||
130 | __be16 frUnused; | ||
131 | __be16 frComment; | ||
132 | __be32 frPutAway; | ||
133 | } __packed; | ||
134 | |||
135 | union hfs_finder_info { | ||
136 | struct { | ||
137 | struct hfs_finfo finfo; | ||
138 | struct hfs_fxinfo fxinfo; | ||
139 | } file; | ||
140 | struct { | ||
141 | struct hfs_dinfo dinfo; | ||
142 | struct hfs_dxinfo dxinfo; | ||
143 | } dir; | ||
144 | } __packed; | ||
145 | |||
146 | /* Cast to a pointer to a generic bkey */ | ||
147 | #define HFS_BKEY(X) (((void)((X)->KeyLen)), ((struct hfs_bkey *)(X))) | ||
148 | |||
149 | /* The key used in the catalog b-tree: */ | ||
150 | struct hfs_cat_key { | ||
151 | u8 key_len; /* number of bytes in the key */ | ||
152 | u8 reserved; /* padding */ | ||
153 | __be32 ParID; /* CNID of the parent dir */ | ||
154 | struct hfs_name CName; /* The filename of the entry */ | ||
155 | } __packed; | ||
156 | |||
157 | /* The key used in the extents b-tree: */ | ||
158 | struct hfs_ext_key { | ||
159 | u8 key_len; /* number of bytes in the key */ | ||
160 | u8 FkType; /* HFS_FK_{DATA,RSRC} */ | ||
161 | __be32 FNum; /* The File ID of the file */ | ||
162 | __be16 FABN; /* allocation blocks number*/ | ||
163 | } __packed; | ||
164 | |||
165 | typedef union hfs_btree_key { | ||
166 | u8 key_len; /* number of bytes in the key */ | ||
167 | struct hfs_cat_key cat; | ||
168 | struct hfs_ext_key ext; | ||
169 | } hfs_btree_key; | ||
170 | |||
171 | typedef union hfs_btree_key btree_key; | ||
172 | |||
173 | struct hfs_extent { | ||
174 | __be16 block; | ||
175 | __be16 count; | ||
176 | }; | ||
177 | typedef struct hfs_extent hfs_extent_rec[3]; | ||
178 | |||
179 | /* The catalog record for a file */ | ||
180 | struct hfs_cat_file { | ||
181 | s8 type; /* The type of entry */ | ||
182 | u8 reserved; | ||
183 | u8 Flags; /* Flags such as read-only */ | ||
184 | s8 Typ; /* file version number = 0 */ | ||
185 | struct hfs_finfo UsrWds; /* data used by the Finder */ | ||
186 | __be32 FlNum; /* The CNID */ | ||
187 | __be16 StBlk; /* obsolete */ | ||
188 | __be32 LgLen; /* The logical EOF of the data fork*/ | ||
189 | __be32 PyLen; /* The physical EOF of the data fork */ | ||
190 | __be16 RStBlk; /* obsolete */ | ||
191 | __be32 RLgLen; /* The logical EOF of the rsrc fork */ | ||
192 | __be32 RPyLen; /* The physical EOF of the rsrc fork */ | ||
193 | __be32 CrDat; /* The creation date */ | ||
194 | __be32 MdDat; /* The modified date */ | ||
195 | __be32 BkDat; /* The last backup date */ | ||
196 | struct hfs_fxinfo FndrInfo; /* more data for the Finder */ | ||
197 | __be16 ClpSize; /* number of bytes to allocate | ||
198 | when extending files */ | ||
199 | hfs_extent_rec ExtRec; /* first extent record | ||
200 | for the data fork */ | ||
201 | hfs_extent_rec RExtRec; /* first extent record | ||
202 | for the resource fork */ | ||
203 | u32 Resrv; /* reserved by Apple */ | ||
204 | } __packed; | ||
205 | |||
206 | /* the catalog record for a directory */ | ||
207 | struct hfs_cat_dir { | ||
208 | s8 type; /* The type of entry */ | ||
209 | u8 reserved; | ||
210 | __be16 Flags; /* flags */ | ||
211 | __be16 Val; /* Valence: number of files and | ||
212 | dirs in the directory */ | ||
213 | __be32 DirID; /* The CNID */ | ||
214 | __be32 CrDat; /* The creation date */ | ||
215 | __be32 MdDat; /* The modification date */ | ||
216 | __be32 BkDat; /* The last backup date */ | ||
217 | struct hfs_dinfo UsrInfo; /* data used by the Finder */ | ||
218 | struct hfs_dxinfo FndrInfo; /* more data used by Finder */ | ||
219 | u8 Resrv[16]; /* reserved by Apple */ | ||
220 | } __packed; | ||
221 | |||
222 | /* the catalog record for a thread */ | ||
223 | struct hfs_cat_thread { | ||
224 | s8 type; /* The type of entry */ | ||
225 | u8 reserved[9]; /* reserved by Apple */ | ||
226 | __be32 ParID; /* CNID of parent directory */ | ||
227 | struct hfs_name CName; /* The name of this entry */ | ||
228 | } __packed; | ||
229 | |||
230 | /* A catalog tree record */ | ||
231 | typedef union hfs_cat_rec { | ||
232 | s8 type; /* The type of entry */ | ||
233 | struct hfs_cat_file file; | ||
234 | struct hfs_cat_dir dir; | ||
235 | struct hfs_cat_thread thread; | ||
236 | } hfs_cat_rec; | ||
237 | |||
238 | struct hfs_mdb { | ||
239 | __be16 drSigWord; /* Signature word indicating fs type */ | ||
240 | __be32 drCrDate; /* fs creation date/time */ | ||
241 | __be32 drLsMod; /* fs modification date/time */ | ||
242 | __be16 drAtrb; /* fs attributes */ | ||
243 | __be16 drNmFls; /* number of files in root directory */ | ||
244 | __be16 drVBMSt; /* location (in 512-byte blocks) | ||
245 | of the volume bitmap */ | ||
246 | __be16 drAllocPtr; /* location (in allocation blocks) | ||
247 | to begin next allocation search */ | ||
248 | __be16 drNmAlBlks; /* number of allocation blocks */ | ||
249 | __be32 drAlBlkSiz; /* bytes in an allocation block */ | ||
250 | __be32 drClpSiz; /* clumpsize, the number of bytes to | ||
251 | allocate when extending a file */ | ||
252 | __be16 drAlBlSt; /* location (in 512-byte blocks) | ||
253 | of the first allocation block */ | ||
254 | __be32 drNxtCNID; /* CNID to assign to the next | ||
255 | file or directory created */ | ||
256 | __be16 drFreeBks; /* number of free allocation blocks */ | ||
257 | u8 drVN[28]; /* the volume label */ | ||
258 | __be32 drVolBkUp; /* fs backup date/time */ | ||
259 | __be16 drVSeqNum; /* backup sequence number */ | ||
260 | __be32 drWrCnt; /* fs write count */ | ||
261 | __be32 drXTClpSiz; /* clumpsize for the extents B-tree */ | ||
262 | __be32 drCTClpSiz; /* clumpsize for the catalog B-tree */ | ||
263 | __be16 drNmRtDirs; /* number of directories in | ||
264 | the root directory */ | ||
265 | __be32 drFilCnt; /* number of files in the fs */ | ||
266 | __be32 drDirCnt; /* number of directories in the fs */ | ||
267 | u8 drFndrInfo[32]; /* data used by the Finder */ | ||
268 | __be16 drEmbedSigWord; /* embedded volume signature */ | ||
269 | __be32 drEmbedExtent; /* starting block number (xdrStABN) | ||
270 | and number of allocation blocks | ||
271 | (xdrNumABlks) occupied by embedded | ||
272 | volume */ | ||
273 | __be32 drXTFlSize; /* bytes in the extents B-tree */ | ||
274 | hfs_extent_rec drXTExtRec; /* extents B-tree's first 3 extents */ | ||
275 | __be32 drCTFlSize; /* bytes in the catalog B-tree */ | ||
276 | hfs_extent_rec drCTExtRec; /* catalog B-tree's first 3 extents */ | ||
277 | } __packed; | ||
278 | |||
279 | /*======== Data structures kept in memory ========*/ | ||
280 | |||
281 | struct hfs_readdir_data { | ||
282 | struct list_head list; | ||
283 | struct file *file; | ||
284 | struct hfs_cat_key key; | ||
285 | }; | ||
286 | |||
287 | #endif | ||
diff --git a/fs/hfs/hfs_fs.h b/fs/hfs/hfs_fs.h new file mode 100644 index 000000000000..0dc8ef8e14de --- /dev/null +++ b/fs/hfs/hfs_fs.h | |||
@@ -0,0 +1,286 @@ | |||
1 | /* | ||
2 | * linux/fs/hfs/hfs_fs.h | ||
3 | * | ||
4 | * Copyright (C) 1995-1997 Paul H. Hargrove | ||
5 | * (C) 2003 Ardis Technologies <roman@ardistech.com> | ||
6 | * This file may be distributed under the terms of the GNU General Public License. | ||
7 | */ | ||
8 | |||
9 | #ifndef _LINUX_HFS_FS_H | ||
10 | #define _LINUX_HFS_FS_H | ||
11 | |||
12 | #include <linux/version.h> | ||
13 | #include <linux/slab.h> | ||
14 | #include <linux/types.h> | ||
15 | #include <linux/buffer_head.h> | ||
16 | #include <linux/fs.h> | ||
17 | |||
18 | #include <asm/byteorder.h> | ||
19 | #include <asm/uaccess.h> | ||
20 | |||
21 | #include "hfs.h" | ||
22 | |||
23 | #define DBG_BNODE_REFS 0x00000001 | ||
24 | #define DBG_BNODE_MOD 0x00000002 | ||
25 | #define DBG_CAT_MOD 0x00000004 | ||
26 | #define DBG_INODE 0x00000008 | ||
27 | #define DBG_SUPER 0x00000010 | ||
28 | #define DBG_EXTENT 0x00000020 | ||
29 | #define DBG_BITMAP 0x00000040 | ||
30 | |||
31 | //#define DBG_MASK (DBG_EXTENT|DBG_INODE|DBG_BNODE_MOD|DBG_CAT_MOD|DBG_BITMAP) | ||
32 | //#define DBG_MASK (DBG_BNODE_MOD|DBG_CAT_MOD|DBG_INODE) | ||
33 | //#define DBG_MASK (DBG_CAT_MOD|DBG_BNODE_REFS|DBG_INODE|DBG_EXTENT) | ||
34 | #define DBG_MASK (0) | ||
35 | |||
36 | #define dprint(flg, fmt, args...) \ | ||
37 | if (flg & DBG_MASK) printk(fmt , ## args) | ||
38 | |||
39 | #define hfs_warn(format, args...) printk(KERN_WARNING format , ## args) | ||
40 | #define hfs_error(format, args...) printk(KERN_ERR format , ## args) | ||
41 | |||
42 | /* | ||
43 | * struct hfs_inode_info | ||
44 | * | ||
45 | * The HFS-specific part of a Linux (struct inode) | ||
46 | */ | ||
47 | struct hfs_inode_info { | ||
48 | atomic_t opencnt; | ||
49 | |||
50 | unsigned int flags; | ||
51 | |||
52 | /* to deal with localtime ugliness */ | ||
53 | int tz_secondswest; | ||
54 | |||
55 | struct hfs_cat_key cat_key; | ||
56 | |||
57 | struct list_head open_dir_list; | ||
58 | struct inode *rsrc_inode; | ||
59 | |||
60 | struct semaphore extents_lock; | ||
61 | |||
62 | u16 alloc_blocks, clump_blocks; | ||
63 | sector_t fs_blocks; | ||
64 | /* Allocation extents from catlog record or volume header */ | ||
65 | hfs_extent_rec first_extents; | ||
66 | u16 first_blocks; | ||
67 | hfs_extent_rec cached_extents; | ||
68 | u16 cached_start, cached_blocks; | ||
69 | |||
70 | loff_t phys_size; | ||
71 | struct inode vfs_inode; | ||
72 | }; | ||
73 | |||
74 | #define HFS_FLG_RSRC 0x0001 | ||
75 | #define HFS_FLG_EXT_DIRTY 0x0002 | ||
76 | #define HFS_FLG_EXT_NEW 0x0004 | ||
77 | |||
78 | #define HFS_IS_RSRC(inode) (HFS_I(inode)->flags & HFS_FLG_RSRC) | ||
79 | |||
80 | /* | ||
81 | * struct hfs_sb_info | ||
82 | * | ||
83 | * The HFS-specific part of a Linux (struct super_block) | ||
84 | */ | ||
85 | struct hfs_sb_info { | ||
86 | struct buffer_head *mdb_bh; /* The hfs_buffer | ||
87 | holding the real | ||
88 | superblock (aka VIB | ||
89 | or MDB) */ | ||
90 | struct hfs_mdb *mdb; | ||
91 | struct buffer_head *alt_mdb_bh; /* The hfs_buffer holding | ||
92 | the alternate superblock */ | ||
93 | struct hfs_mdb *alt_mdb; | ||
94 | __be32 *bitmap; /* The page holding the | ||
95 | allocation bitmap */ | ||
96 | struct hfs_btree *ext_tree; /* Information about | ||
97 | the extents b-tree */ | ||
98 | struct hfs_btree *cat_tree; /* Information about | ||
99 | the catalog b-tree */ | ||
100 | u32 file_count; /* The number of | ||
101 | regular files in | ||
102 | the filesystem */ | ||
103 | u32 folder_count; /* The number of | ||
104 | directories in the | ||
105 | filesystem */ | ||
106 | u32 next_id; /* The next available | ||
107 | file id number */ | ||
108 | u32 clumpablks; /* The number of allocation | ||
109 | blocks to try to add when | ||
110 | extending a file */ | ||
111 | u32 fs_start; /* The first 512-byte | ||
112 | block represented | ||
113 | in the bitmap */ | ||
114 | u32 part_start; | ||
115 | u16 root_files; /* The number of | ||
116 | regular | ||
117 | (non-directory) | ||
118 | files in the root | ||
119 | directory */ | ||
120 | u16 root_dirs; /* The number of | ||
121 | directories in the | ||
122 | root directory */ | ||
123 | u16 fs_ablocks; /* The number of | ||
124 | allocation blocks | ||
125 | in the filesystem */ | ||
126 | u16 free_ablocks; /* the number of unused | ||
127 | allocation blocks | ||
128 | in the filesystem */ | ||
129 | u32 alloc_blksz; /* The size of an | ||
130 | "allocation block" */ | ||
131 | int s_quiet; /* Silent failure when | ||
132 | changing owner or mode? */ | ||
133 | __be32 s_type; /* Type for new files */ | ||
134 | __be32 s_creator; /* Creator for new files */ | ||
135 | umode_t s_file_umask; /* The umask applied to the | ||
136 | permissions on all files */ | ||
137 | umode_t s_dir_umask; /* The umask applied to the | ||
138 | permissions on all dirs */ | ||
139 | uid_t s_uid; /* The uid of all files */ | ||
140 | gid_t s_gid; /* The gid of all files */ | ||
141 | |||
142 | int session, part; | ||
143 | |||
144 | struct semaphore bitmap_lock; | ||
145 | |||
146 | unsigned long flags; | ||
147 | |||
148 | u16 blockoffset; | ||
149 | |||
150 | int fs_div; | ||
151 | |||
152 | struct hlist_head rsrc_inodes; | ||
153 | }; | ||
154 | |||
155 | #define HFS_FLG_BITMAP_DIRTY 0 | ||
156 | #define HFS_FLG_MDB_DIRTY 1 | ||
157 | #define HFS_FLG_ALT_MDB_DIRTY 2 | ||
158 | |||
159 | /* bitmap.c */ | ||
160 | extern u32 hfs_vbm_search_free(struct super_block *, u32, u32 *); | ||
161 | extern int hfs_clear_vbm_bits(struct super_block *, u16, u16); | ||
162 | |||
163 | /* catalog.c */ | ||
164 | extern int hfs_cat_keycmp(const btree_key *, const btree_key *); | ||
165 | struct hfs_find_data; | ||
166 | extern int hfs_cat_find_brec(struct super_block *, u32, struct hfs_find_data *); | ||
167 | extern int hfs_cat_create(u32, struct inode *, struct qstr *, struct inode *); | ||
168 | extern int hfs_cat_delete(u32, struct inode *, struct qstr *); | ||
169 | extern int hfs_cat_move(u32, struct inode *, struct qstr *, | ||
170 | struct inode *, struct qstr *); | ||
171 | extern void hfs_cat_build_key(btree_key *, u32, struct qstr *); | ||
172 | |||
173 | /* dir.c */ | ||
174 | extern struct file_operations hfs_dir_operations; | ||
175 | extern struct inode_operations hfs_dir_inode_operations; | ||
176 | |||
177 | /* extent.c */ | ||
178 | extern int hfs_ext_keycmp(const btree_key *, const btree_key *); | ||
179 | extern int hfs_free_fork(struct super_block *, struct hfs_cat_file *, int); | ||
180 | extern void hfs_ext_write_extent(struct inode *); | ||
181 | extern int hfs_extend_file(struct inode *); | ||
182 | extern void hfs_file_truncate(struct inode *); | ||
183 | |||
184 | extern int hfs_get_block(struct inode *, sector_t, struct buffer_head *, int); | ||
185 | |||
186 | /* inode.c */ | ||
187 | extern struct address_space_operations hfs_aops; | ||
188 | extern struct address_space_operations hfs_btree_aops; | ||
189 | |||
190 | extern struct inode *hfs_new_inode(struct inode *, struct qstr *, int); | ||
191 | extern void hfs_inode_write_fork(struct inode *, struct hfs_extent *, __be32 *, __be32 *); | ||
192 | extern int hfs_write_inode(struct inode *, int); | ||
193 | extern int hfs_inode_setattr(struct dentry *, struct iattr *); | ||
194 | extern void hfs_inode_read_fork(struct inode *inode, struct hfs_extent *ext, | ||
195 | __be32 log_size, __be32 phys_size, u32 clump_size); | ||
196 | extern struct inode *hfs_iget(struct super_block *, struct hfs_cat_key *, hfs_cat_rec *); | ||
197 | extern void hfs_clear_inode(struct inode *); | ||
198 | extern void hfs_delete_inode(struct inode *); | ||
199 | |||
200 | /* attr.c */ | ||
201 | extern int hfs_setxattr(struct dentry *dentry, const char *name, | ||
202 | const void *value, size_t size, int flags); | ||
203 | extern ssize_t hfs_getxattr(struct dentry *dentry, const char *name, | ||
204 | void *value, size_t size); | ||
205 | extern ssize_t hfs_listxattr(struct dentry *dentry, char *buffer, size_t size); | ||
206 | |||
207 | /* mdb.c */ | ||
208 | extern int hfs_mdb_get(struct super_block *); | ||
209 | extern void hfs_mdb_commit(struct super_block *); | ||
210 | extern void hfs_mdb_close(struct super_block *); | ||
211 | extern void hfs_mdb_put(struct super_block *); | ||
212 | |||
213 | /* part_tbl.c */ | ||
214 | extern int hfs_part_find(struct super_block *, sector_t *, sector_t *); | ||
215 | |||
216 | /* string.c */ | ||
217 | extern struct dentry_operations hfs_dentry_operations; | ||
218 | |||
219 | extern int hfs_hash_dentry(struct dentry *, struct qstr *); | ||
220 | extern int hfs_strcmp(const unsigned char *, unsigned int, | ||
221 | const unsigned char *, unsigned int); | ||
222 | extern int hfs_compare_dentry(struct dentry *, struct qstr *, struct qstr *); | ||
223 | |||
224 | /* trans.c */ | ||
225 | extern void hfs_triv2mac(struct hfs_name *, struct qstr *); | ||
226 | extern int hfs_mac2triv(char *, const struct hfs_name *); | ||
227 | |||
228 | extern struct timezone sys_tz; | ||
229 | |||
230 | /* | ||
231 | * There are two time systems. Both are based on seconds since | ||
232 | * a particular time/date. | ||
233 | * Unix: unsigned lil-endian since 00:00 GMT, Jan. 1, 1970 | ||
234 | * mac: unsigned big-endian since 00:00 GMT, Jan. 1, 1904 | ||
235 | * | ||
236 | */ | ||
237 | #define __hfs_u_to_mtime(sec) cpu_to_be32(sec + 2082844800U - sys_tz.tz_minuteswest * 60) | ||
238 | #define __hfs_m_to_utime(sec) (be32_to_cpu(sec) - 2082844800U + sys_tz.tz_minuteswest * 60) | ||
239 | |||
240 | #define HFS_I(inode) (list_entry(inode, struct hfs_inode_info, vfs_inode)) | ||
241 | #define HFS_SB(sb) ((struct hfs_sb_info *)(sb)->s_fs_info) | ||
242 | |||
243 | #define hfs_m_to_utime(time) (struct timespec){ .tv_sec = __hfs_m_to_utime(time) } | ||
244 | #define hfs_u_to_mtime(time) __hfs_u_to_mtime((time).tv_sec) | ||
245 | #define hfs_mtime() __hfs_u_to_mtime(get_seconds()) | ||
246 | |||
247 | static inline const char *hfs_mdb_name(struct super_block *sb) | ||
248 | { | ||
249 | return sb->s_id; | ||
250 | } | ||
251 | |||
252 | static inline void hfs_bitmap_dirty(struct super_block *sb) | ||
253 | { | ||
254 | set_bit(HFS_FLG_BITMAP_DIRTY, &HFS_SB(sb)->flags); | ||
255 | sb->s_dirt = 1; | ||
256 | } | ||
257 | |||
258 | static inline void hfs_buffer_sync(struct buffer_head *bh) | ||
259 | { | ||
260 | while (buffer_locked(bh)) { | ||
261 | wait_on_buffer(bh); | ||
262 | } | ||
263 | if (buffer_dirty(bh)) { | ||
264 | ll_rw_block(WRITE, 1, &bh); | ||
265 | wait_on_buffer(bh); | ||
266 | } | ||
267 | } | ||
268 | |||
269 | #define sb_bread512(sb, sec, data) ({ \ | ||
270 | struct buffer_head *__bh; \ | ||
271 | sector_t __block; \ | ||
272 | loff_t __start; \ | ||
273 | int __offset; \ | ||
274 | \ | ||
275 | __start = (loff_t)(sec) << HFS_SECTOR_SIZE_BITS;\ | ||
276 | __block = __start >> (sb)->s_blocksize_bits; \ | ||
277 | __offset = __start & ((sb)->s_blocksize - 1); \ | ||
278 | __bh = sb_bread((sb), __block); \ | ||
279 | if (likely(__bh != NULL)) \ | ||
280 | data = (void *)(__bh->b_data + __offset);\ | ||
281 | else \ | ||
282 | data = NULL; \ | ||
283 | __bh; \ | ||
284 | }) | ||
285 | |||
286 | #endif | ||
diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c new file mode 100644 index 000000000000..751912326094 --- /dev/null +++ b/fs/hfs/inode.c | |||
@@ -0,0 +1,636 @@ | |||
1 | /* | ||
2 | * linux/fs/hfs/inode.c | ||
3 | * | ||
4 | * Copyright (C) 1995-1997 Paul H. Hargrove | ||
5 | * (C) 2003 Ardis Technologies <roman@ardistech.com> | ||
6 | * This file may be distributed under the terms of the GNU General Public License. | ||
7 | * | ||
8 | * This file contains inode-related functions which do not depend on | ||
9 | * which scheme is being used to represent forks. | ||
10 | * | ||
11 | * Based on the minix file system code, (C) 1991, 1992 by Linus Torvalds | ||
12 | */ | ||
13 | |||
14 | #include <linux/pagemap.h> | ||
15 | #include <linux/version.h> | ||
16 | #include <linux/mpage.h> | ||
17 | |||
18 | #include "hfs_fs.h" | ||
19 | #include "btree.h" | ||
20 | |||
21 | static struct file_operations hfs_file_operations; | ||
22 | static struct inode_operations hfs_file_inode_operations; | ||
23 | |||
24 | /*================ Variable-like macros ================*/ | ||
25 | |||
26 | #define HFS_VALID_MODE_BITS (S_IFREG | S_IFDIR | S_IRWXUGO) | ||
27 | |||
28 | static int hfs_writepage(struct page *page, struct writeback_control *wbc) | ||
29 | { | ||
30 | return block_write_full_page(page, hfs_get_block, wbc); | ||
31 | } | ||
32 | |||
33 | static int hfs_readpage(struct file *file, struct page *page) | ||
34 | { | ||
35 | return block_read_full_page(page, hfs_get_block); | ||
36 | } | ||
37 | |||
38 | static int hfs_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to) | ||
39 | { | ||
40 | return cont_prepare_write(page, from, to, hfs_get_block, | ||
41 | &HFS_I(page->mapping->host)->phys_size); | ||
42 | } | ||
43 | |||
44 | static sector_t hfs_bmap(struct address_space *mapping, sector_t block) | ||
45 | { | ||
46 | return generic_block_bmap(mapping, block, hfs_get_block); | ||
47 | } | ||
48 | |||
49 | static int hfs_releasepage(struct page *page, int mask) | ||
50 | { | ||
51 | struct inode *inode = page->mapping->host; | ||
52 | struct super_block *sb = inode->i_sb; | ||
53 | struct hfs_btree *tree; | ||
54 | struct hfs_bnode *node; | ||
55 | u32 nidx; | ||
56 | int i, res = 1; | ||
57 | |||
58 | switch (inode->i_ino) { | ||
59 | case HFS_EXT_CNID: | ||
60 | tree = HFS_SB(sb)->ext_tree; | ||
61 | break; | ||
62 | case HFS_CAT_CNID: | ||
63 | tree = HFS_SB(sb)->cat_tree; | ||
64 | break; | ||
65 | default: | ||
66 | BUG(); | ||
67 | return 0; | ||
68 | } | ||
69 | if (tree->node_size >= PAGE_CACHE_SIZE) { | ||
70 | nidx = page->index >> (tree->node_size_shift - PAGE_CACHE_SHIFT); | ||
71 | spin_lock(&tree->hash_lock); | ||
72 | node = hfs_bnode_findhash(tree, nidx); | ||
73 | if (!node) | ||
74 | ; | ||
75 | else if (atomic_read(&node->refcnt)) | ||
76 | res = 0; | ||
77 | if (res && node) { | ||
78 | hfs_bnode_unhash(node); | ||
79 | hfs_bnode_free(node); | ||
80 | } | ||
81 | spin_unlock(&tree->hash_lock); | ||
82 | } else { | ||
83 | nidx = page->index << (PAGE_CACHE_SHIFT - tree->node_size_shift); | ||
84 | i = 1 << (PAGE_CACHE_SHIFT - tree->node_size_shift); | ||
85 | spin_lock(&tree->hash_lock); | ||
86 | do { | ||
87 | node = hfs_bnode_findhash(tree, nidx++); | ||
88 | if (!node) | ||
89 | continue; | ||
90 | if (atomic_read(&node->refcnt)) { | ||
91 | res = 0; | ||
92 | break; | ||
93 | } | ||
94 | hfs_bnode_unhash(node); | ||
95 | hfs_bnode_free(node); | ||
96 | } while (--i && nidx < tree->node_count); | ||
97 | spin_unlock(&tree->hash_lock); | ||
98 | } | ||
99 | //printk("releasepage: %lu,%x = %d\n", page->index, mask, res); | ||
100 | return res ? try_to_free_buffers(page) : 0; | ||
101 | } | ||
102 | |||
103 | static int hfs_get_blocks(struct inode *inode, sector_t iblock, unsigned long max_blocks, | ||
104 | struct buffer_head *bh_result, int create) | ||
105 | { | ||
106 | int ret; | ||
107 | |||
108 | ret = hfs_get_block(inode, iblock, bh_result, create); | ||
109 | if (!ret) | ||
110 | bh_result->b_size = (1 << inode->i_blkbits); | ||
111 | return ret; | ||
112 | } | ||
113 | |||
114 | static ssize_t hfs_direct_IO(int rw, struct kiocb *iocb, | ||
115 | const struct iovec *iov, loff_t offset, unsigned long nr_segs) | ||
116 | { | ||
117 | struct file *file = iocb->ki_filp; | ||
118 | struct inode *inode = file->f_dentry->d_inode->i_mapping->host; | ||
119 | |||
120 | return blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov, | ||
121 | offset, nr_segs, hfs_get_blocks, NULL); | ||
122 | } | ||
123 | |||
124 | static int hfs_writepages(struct address_space *mapping, | ||
125 | struct writeback_control *wbc) | ||
126 | { | ||
127 | return mpage_writepages(mapping, wbc, hfs_get_block); | ||
128 | } | ||
129 | |||
130 | struct address_space_operations hfs_btree_aops = { | ||
131 | .readpage = hfs_readpage, | ||
132 | .writepage = hfs_writepage, | ||
133 | .sync_page = block_sync_page, | ||
134 | .prepare_write = hfs_prepare_write, | ||
135 | .commit_write = generic_commit_write, | ||
136 | .bmap = hfs_bmap, | ||
137 | .releasepage = hfs_releasepage, | ||
138 | }; | ||
139 | |||
140 | struct address_space_operations hfs_aops = { | ||
141 | .readpage = hfs_readpage, | ||
142 | .writepage = hfs_writepage, | ||
143 | .sync_page = block_sync_page, | ||
144 | .prepare_write = hfs_prepare_write, | ||
145 | .commit_write = generic_commit_write, | ||
146 | .bmap = hfs_bmap, | ||
147 | .direct_IO = hfs_direct_IO, | ||
148 | .writepages = hfs_writepages, | ||
149 | }; | ||
150 | |||
151 | /* | ||
152 | * hfs_new_inode | ||
153 | */ | ||
154 | struct inode *hfs_new_inode(struct inode *dir, struct qstr *name, int mode) | ||
155 | { | ||
156 | struct super_block *sb = dir->i_sb; | ||
157 | struct inode *inode = new_inode(sb); | ||
158 | if (!inode) | ||
159 | return NULL; | ||
160 | |||
161 | init_MUTEX(&HFS_I(inode)->extents_lock); | ||
162 | INIT_LIST_HEAD(&HFS_I(inode)->open_dir_list); | ||
163 | hfs_cat_build_key((btree_key *)&HFS_I(inode)->cat_key, dir->i_ino, name); | ||
164 | inode->i_ino = HFS_SB(sb)->next_id++; | ||
165 | inode->i_mode = mode; | ||
166 | inode->i_uid = current->fsuid; | ||
167 | inode->i_gid = current->fsgid; | ||
168 | inode->i_nlink = 1; | ||
169 | inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME_SEC; | ||
170 | inode->i_blksize = HFS_SB(sb)->alloc_blksz; | ||
171 | HFS_I(inode)->flags = 0; | ||
172 | HFS_I(inode)->rsrc_inode = NULL; | ||
173 | HFS_I(inode)->fs_blocks = 0; | ||
174 | if (S_ISDIR(mode)) { | ||
175 | inode->i_size = 2; | ||
176 | HFS_SB(sb)->folder_count++; | ||
177 | if (dir->i_ino == HFS_ROOT_CNID) | ||
178 | HFS_SB(sb)->root_dirs++; | ||
179 | inode->i_op = &hfs_dir_inode_operations; | ||
180 | inode->i_fop = &hfs_dir_operations; | ||
181 | inode->i_mode |= S_IRWXUGO; | ||
182 | inode->i_mode &= ~HFS_SB(inode->i_sb)->s_dir_umask; | ||
183 | } else if (S_ISREG(mode)) { | ||
184 | HFS_I(inode)->clump_blocks = HFS_SB(sb)->clumpablks; | ||
185 | HFS_SB(sb)->file_count++; | ||
186 | if (dir->i_ino == HFS_ROOT_CNID) | ||
187 | HFS_SB(sb)->root_files++; | ||
188 | inode->i_op = &hfs_file_inode_operations; | ||
189 | inode->i_fop = &hfs_file_operations; | ||
190 | inode->i_mapping->a_ops = &hfs_aops; | ||
191 | inode->i_mode |= S_IRUGO|S_IXUGO; | ||
192 | if (mode & S_IWUSR) | ||
193 | inode->i_mode |= S_IWUGO; | ||
194 | inode->i_mode &= ~HFS_SB(inode->i_sb)->s_file_umask; | ||
195 | HFS_I(inode)->phys_size = 0; | ||
196 | HFS_I(inode)->alloc_blocks = 0; | ||
197 | HFS_I(inode)->first_blocks = 0; | ||
198 | HFS_I(inode)->cached_start = 0; | ||
199 | HFS_I(inode)->cached_blocks = 0; | ||
200 | memset(HFS_I(inode)->first_extents, 0, sizeof(hfs_extent_rec)); | ||
201 | memset(HFS_I(inode)->cached_extents, 0, sizeof(hfs_extent_rec)); | ||
202 | } | ||
203 | insert_inode_hash(inode); | ||
204 | mark_inode_dirty(inode); | ||
205 | set_bit(HFS_FLG_MDB_DIRTY, &HFS_SB(sb)->flags); | ||
206 | sb->s_dirt = 1; | ||
207 | |||
208 | return inode; | ||
209 | } | ||
210 | |||
211 | void hfs_delete_inode(struct inode *inode) | ||
212 | { | ||
213 | struct super_block *sb = inode->i_sb; | ||
214 | |||
215 | dprint(DBG_INODE, "delete_inode: %lu\n", inode->i_ino); | ||
216 | if (S_ISDIR(inode->i_mode)) { | ||
217 | HFS_SB(sb)->folder_count--; | ||
218 | if (HFS_I(inode)->cat_key.ParID == cpu_to_be32(HFS_ROOT_CNID)) | ||
219 | HFS_SB(sb)->root_dirs--; | ||
220 | set_bit(HFS_FLG_MDB_DIRTY, &HFS_SB(sb)->flags); | ||
221 | sb->s_dirt = 1; | ||
222 | return; | ||
223 | } | ||
224 | HFS_SB(sb)->file_count--; | ||
225 | if (HFS_I(inode)->cat_key.ParID == cpu_to_be32(HFS_ROOT_CNID)) | ||
226 | HFS_SB(sb)->root_files--; | ||
227 | if (S_ISREG(inode->i_mode)) { | ||
228 | if (!inode->i_nlink) { | ||
229 | inode->i_size = 0; | ||
230 | hfs_file_truncate(inode); | ||
231 | } | ||
232 | } | ||
233 | set_bit(HFS_FLG_MDB_DIRTY, &HFS_SB(sb)->flags); | ||
234 | sb->s_dirt = 1; | ||
235 | } | ||
236 | |||
237 | void hfs_inode_read_fork(struct inode *inode, struct hfs_extent *ext, | ||
238 | __be32 __log_size, __be32 phys_size, u32 clump_size) | ||
239 | { | ||
240 | struct super_block *sb = inode->i_sb; | ||
241 | u32 log_size = be32_to_cpu(__log_size); | ||
242 | u16 count; | ||
243 | int i; | ||
244 | |||
245 | memcpy(HFS_I(inode)->first_extents, ext, sizeof(hfs_extent_rec)); | ||
246 | for (count = 0, i = 0; i < 3; i++) | ||
247 | count += be16_to_cpu(ext[i].count); | ||
248 | HFS_I(inode)->first_blocks = count; | ||
249 | |||
250 | inode->i_size = HFS_I(inode)->phys_size = log_size; | ||
251 | HFS_I(inode)->fs_blocks = (log_size + sb->s_blocksize - 1) >> sb->s_blocksize_bits; | ||
252 | inode_set_bytes(inode, HFS_I(inode)->fs_blocks << sb->s_blocksize_bits); | ||
253 | HFS_I(inode)->alloc_blocks = be32_to_cpu(phys_size) / | ||
254 | HFS_SB(sb)->alloc_blksz; | ||
255 | HFS_I(inode)->clump_blocks = clump_size / HFS_SB(sb)->alloc_blksz; | ||
256 | if (!HFS_I(inode)->clump_blocks) | ||
257 | HFS_I(inode)->clump_blocks = HFS_SB(sb)->clumpablks; | ||
258 | } | ||
259 | |||
260 | struct hfs_iget_data { | ||
261 | struct hfs_cat_key *key; | ||
262 | hfs_cat_rec *rec; | ||
263 | }; | ||
264 | |||
265 | static int hfs_test_inode(struct inode *inode, void *data) | ||
266 | { | ||
267 | struct hfs_iget_data *idata = data; | ||
268 | hfs_cat_rec *rec; | ||
269 | |||
270 | rec = idata->rec; | ||
271 | switch (rec->type) { | ||
272 | case HFS_CDR_DIR: | ||
273 | return inode->i_ino == be32_to_cpu(rec->dir.DirID); | ||
274 | case HFS_CDR_FIL: | ||
275 | return inode->i_ino == be32_to_cpu(rec->file.FlNum); | ||
276 | default: | ||
277 | BUG(); | ||
278 | return 1; | ||
279 | } | ||
280 | } | ||
281 | |||
282 | /* | ||
283 | * hfs_read_inode | ||
284 | */ | ||
285 | static int hfs_read_inode(struct inode *inode, void *data) | ||
286 | { | ||
287 | struct hfs_iget_data *idata = data; | ||
288 | struct hfs_sb_info *hsb = HFS_SB(inode->i_sb); | ||
289 | hfs_cat_rec *rec; | ||
290 | |||
291 | HFS_I(inode)->flags = 0; | ||
292 | HFS_I(inode)->rsrc_inode = NULL; | ||
293 | init_MUTEX(&HFS_I(inode)->extents_lock); | ||
294 | INIT_LIST_HEAD(&HFS_I(inode)->open_dir_list); | ||
295 | |||
296 | /* Initialize the inode */ | ||
297 | inode->i_uid = hsb->s_uid; | ||
298 | inode->i_gid = hsb->s_gid; | ||
299 | inode->i_nlink = 1; | ||
300 | inode->i_blksize = HFS_SB(inode->i_sb)->alloc_blksz; | ||
301 | |||
302 | if (idata->key) | ||
303 | HFS_I(inode)->cat_key = *idata->key; | ||
304 | else | ||
305 | HFS_I(inode)->flags |= HFS_FLG_RSRC; | ||
306 | HFS_I(inode)->tz_secondswest = sys_tz.tz_minuteswest * 60; | ||
307 | |||
308 | rec = idata->rec; | ||
309 | switch (rec->type) { | ||
310 | case HFS_CDR_FIL: | ||
311 | if (!HFS_IS_RSRC(inode)) { | ||
312 | hfs_inode_read_fork(inode, rec->file.ExtRec, rec->file.LgLen, | ||
313 | rec->file.PyLen, be16_to_cpu(rec->file.ClpSize)); | ||
314 | } else { | ||
315 | hfs_inode_read_fork(inode, rec->file.RExtRec, rec->file.RLgLen, | ||
316 | rec->file.RPyLen, be16_to_cpu(rec->file.ClpSize)); | ||
317 | } | ||
318 | |||
319 | inode->i_ino = be32_to_cpu(rec->file.FlNum); | ||
320 | inode->i_mode = S_IRUGO | S_IXUGO; | ||
321 | if (!(rec->file.Flags & HFS_FIL_LOCK)) | ||
322 | inode->i_mode |= S_IWUGO; | ||
323 | inode->i_mode &= ~hsb->s_file_umask; | ||
324 | inode->i_mode |= S_IFREG; | ||
325 | inode->i_ctime = inode->i_atime = inode->i_mtime = | ||
326 | hfs_m_to_utime(rec->file.MdDat); | ||
327 | inode->i_op = &hfs_file_inode_operations; | ||
328 | inode->i_fop = &hfs_file_operations; | ||
329 | inode->i_mapping->a_ops = &hfs_aops; | ||
330 | break; | ||
331 | case HFS_CDR_DIR: | ||
332 | inode->i_ino = be32_to_cpu(rec->dir.DirID); | ||
333 | inode->i_size = be16_to_cpu(rec->dir.Val) + 2; | ||
334 | HFS_I(inode)->fs_blocks = 0; | ||
335 | inode->i_mode = S_IFDIR | (S_IRWXUGO & ~hsb->s_dir_umask); | ||
336 | inode->i_ctime = inode->i_atime = inode->i_mtime = | ||
337 | hfs_m_to_utime(rec->dir.MdDat); | ||
338 | inode->i_op = &hfs_dir_inode_operations; | ||
339 | inode->i_fop = &hfs_dir_operations; | ||
340 | break; | ||
341 | default: | ||
342 | make_bad_inode(inode); | ||
343 | } | ||
344 | return 0; | ||
345 | } | ||
346 | |||
347 | /* | ||
348 | * __hfs_iget() | ||
349 | * | ||
350 | * Given the MDB for a HFS filesystem, a 'key' and an 'entry' in | ||
351 | * the catalog B-tree and the 'type' of the desired file return the | ||
352 | * inode for that file/directory or NULL. Note that 'type' indicates | ||
353 | * whether we want the actual file or directory, or the corresponding | ||
354 | * metadata (AppleDouble header file or CAP metadata file). | ||
355 | */ | ||
356 | struct inode *hfs_iget(struct super_block *sb, struct hfs_cat_key *key, hfs_cat_rec *rec) | ||
357 | { | ||
358 | struct hfs_iget_data data = { key, rec }; | ||
359 | struct inode *inode; | ||
360 | u32 cnid; | ||
361 | |||
362 | switch (rec->type) { | ||
363 | case HFS_CDR_DIR: | ||
364 | cnid = be32_to_cpu(rec->dir.DirID); | ||
365 | break; | ||
366 | case HFS_CDR_FIL: | ||
367 | cnid = be32_to_cpu(rec->file.FlNum); | ||
368 | break; | ||
369 | default: | ||
370 | return NULL; | ||
371 | } | ||
372 | inode = iget5_locked(sb, cnid, hfs_test_inode, hfs_read_inode, &data); | ||
373 | if (inode && (inode->i_state & I_NEW)) | ||
374 | unlock_new_inode(inode); | ||
375 | return inode; | ||
376 | } | ||
377 | |||
378 | void hfs_inode_write_fork(struct inode *inode, struct hfs_extent *ext, | ||
379 | __be32 *log_size, __be32 *phys_size) | ||
380 | { | ||
381 | memcpy(ext, HFS_I(inode)->first_extents, sizeof(hfs_extent_rec)); | ||
382 | |||
383 | if (log_size) | ||
384 | *log_size = cpu_to_be32(inode->i_size); | ||
385 | if (phys_size) | ||
386 | *phys_size = cpu_to_be32(HFS_I(inode)->alloc_blocks * | ||
387 | HFS_SB(inode->i_sb)->alloc_blksz); | ||
388 | } | ||
389 | |||
390 | int hfs_write_inode(struct inode *inode, int unused) | ||
391 | { | ||
392 | struct inode *main_inode = inode; | ||
393 | struct hfs_find_data fd; | ||
394 | hfs_cat_rec rec; | ||
395 | |||
396 | dprint(DBG_INODE, "hfs_write_inode: %lu\n", inode->i_ino); | ||
397 | hfs_ext_write_extent(inode); | ||
398 | |||
399 | if (inode->i_ino < HFS_FIRSTUSER_CNID) { | ||
400 | switch (inode->i_ino) { | ||
401 | case HFS_ROOT_CNID: | ||
402 | break; | ||
403 | case HFS_EXT_CNID: | ||
404 | hfs_btree_write(HFS_SB(inode->i_sb)->ext_tree); | ||
405 | return 0; | ||
406 | case HFS_CAT_CNID: | ||
407 | hfs_btree_write(HFS_SB(inode->i_sb)->cat_tree); | ||
408 | return 0; | ||
409 | default: | ||
410 | BUG(); | ||
411 | return -EIO; | ||
412 | } | ||
413 | } | ||
414 | |||
415 | if (HFS_IS_RSRC(inode)) | ||
416 | main_inode = HFS_I(inode)->rsrc_inode; | ||
417 | |||
418 | if (!main_inode->i_nlink) | ||
419 | return 0; | ||
420 | |||
421 | if (hfs_find_init(HFS_SB(main_inode->i_sb)->cat_tree, &fd)) | ||
422 | /* panic? */ | ||
423 | return -EIO; | ||
424 | |||
425 | fd.search_key->cat = HFS_I(main_inode)->cat_key; | ||
426 | if (hfs_brec_find(&fd)) | ||
427 | /* panic? */ | ||
428 | goto out; | ||
429 | |||
430 | if (S_ISDIR(main_inode->i_mode)) { | ||
431 | if (fd.entrylength < sizeof(struct hfs_cat_dir)) | ||
432 | /* panic? */; | ||
433 | hfs_bnode_read(fd.bnode, &rec, fd.entryoffset, | ||
434 | sizeof(struct hfs_cat_dir)); | ||
435 | if (rec.type != HFS_CDR_DIR || | ||
436 | be32_to_cpu(rec.dir.DirID) != inode->i_ino) { | ||
437 | } | ||
438 | |||
439 | rec.dir.MdDat = hfs_u_to_mtime(inode->i_mtime); | ||
440 | rec.dir.Val = cpu_to_be16(inode->i_size - 2); | ||
441 | |||
442 | hfs_bnode_write(fd.bnode, &rec, fd.entryoffset, | ||
443 | sizeof(struct hfs_cat_dir)); | ||
444 | } else if (HFS_IS_RSRC(inode)) { | ||
445 | hfs_bnode_read(fd.bnode, &rec, fd.entryoffset, | ||
446 | sizeof(struct hfs_cat_file)); | ||
447 | hfs_inode_write_fork(inode, rec.file.RExtRec, | ||
448 | &rec.file.RLgLen, &rec.file.RPyLen); | ||
449 | hfs_bnode_write(fd.bnode, &rec, fd.entryoffset, | ||
450 | sizeof(struct hfs_cat_file)); | ||
451 | } else { | ||
452 | if (fd.entrylength < sizeof(struct hfs_cat_file)) | ||
453 | /* panic? */; | ||
454 | hfs_bnode_read(fd.bnode, &rec, fd.entryoffset, | ||
455 | sizeof(struct hfs_cat_file)); | ||
456 | if (rec.type != HFS_CDR_FIL || | ||
457 | be32_to_cpu(rec.file.FlNum) != inode->i_ino) { | ||
458 | } | ||
459 | |||
460 | if (inode->i_mode & S_IWUSR) | ||
461 | rec.file.Flags &= ~HFS_FIL_LOCK; | ||
462 | else | ||
463 | rec.file.Flags |= HFS_FIL_LOCK; | ||
464 | hfs_inode_write_fork(inode, rec.file.ExtRec, &rec.file.LgLen, &rec.file.PyLen); | ||
465 | rec.file.MdDat = hfs_u_to_mtime(inode->i_mtime); | ||
466 | |||
467 | hfs_bnode_write(fd.bnode, &rec, fd.entryoffset, | ||
468 | sizeof(struct hfs_cat_file)); | ||
469 | } | ||
470 | out: | ||
471 | hfs_find_exit(&fd); | ||
472 | return 0; | ||
473 | } | ||
474 | |||
475 | static struct dentry *hfs_file_lookup(struct inode *dir, struct dentry *dentry, | ||
476 | struct nameidata *nd) | ||
477 | { | ||
478 | struct inode *inode = NULL; | ||
479 | hfs_cat_rec rec; | ||
480 | struct hfs_find_data fd; | ||
481 | int res; | ||
482 | |||
483 | if (HFS_IS_RSRC(dir) || strcmp(dentry->d_name.name, "rsrc")) | ||
484 | goto out; | ||
485 | |||
486 | inode = HFS_I(dir)->rsrc_inode; | ||
487 | if (inode) | ||
488 | goto out; | ||
489 | |||
490 | inode = new_inode(dir->i_sb); | ||
491 | if (!inode) | ||
492 | return ERR_PTR(-ENOMEM); | ||
493 | |||
494 | hfs_find_init(HFS_SB(dir->i_sb)->cat_tree, &fd); | ||
495 | fd.search_key->cat = HFS_I(dir)->cat_key; | ||
496 | res = hfs_brec_read(&fd, &rec, sizeof(rec)); | ||
497 | if (!res) { | ||
498 | struct hfs_iget_data idata = { NULL, &rec }; | ||
499 | hfs_read_inode(inode, &idata); | ||
500 | } | ||
501 | hfs_find_exit(&fd); | ||
502 | if (res) { | ||
503 | iput(inode); | ||
504 | return ERR_PTR(res); | ||
505 | } | ||
506 | HFS_I(inode)->rsrc_inode = dir; | ||
507 | HFS_I(dir)->rsrc_inode = inode; | ||
508 | igrab(dir); | ||
509 | hlist_add_head(&inode->i_hash, &HFS_SB(dir->i_sb)->rsrc_inodes); | ||
510 | mark_inode_dirty(inode); | ||
511 | out: | ||
512 | d_add(dentry, inode); | ||
513 | return NULL; | ||
514 | } | ||
515 | |||
516 | void hfs_clear_inode(struct inode *inode) | ||
517 | { | ||
518 | if (HFS_IS_RSRC(inode) && HFS_I(inode)->rsrc_inode) { | ||
519 | HFS_I(HFS_I(inode)->rsrc_inode)->rsrc_inode = NULL; | ||
520 | iput(HFS_I(inode)->rsrc_inode); | ||
521 | } | ||
522 | } | ||
523 | |||
524 | static int hfs_permission(struct inode *inode, int mask, | ||
525 | struct nameidata *nd) | ||
526 | { | ||
527 | if (S_ISREG(inode->i_mode) && mask & MAY_EXEC) | ||
528 | return 0; | ||
529 | return generic_permission(inode, mask, NULL); | ||
530 | } | ||
531 | |||
532 | static int hfs_file_open(struct inode *inode, struct file *file) | ||
533 | { | ||
534 | if (HFS_IS_RSRC(inode)) | ||
535 | inode = HFS_I(inode)->rsrc_inode; | ||
536 | if (atomic_read(&file->f_count) != 1) | ||
537 | return 0; | ||
538 | atomic_inc(&HFS_I(inode)->opencnt); | ||
539 | return 0; | ||
540 | } | ||
541 | |||
542 | static int hfs_file_release(struct inode *inode, struct file *file) | ||
543 | { | ||
544 | //struct super_block *sb = inode->i_sb; | ||
545 | |||
546 | if (HFS_IS_RSRC(inode)) | ||
547 | inode = HFS_I(inode)->rsrc_inode; | ||
548 | if (atomic_read(&file->f_count) != 0) | ||
549 | return 0; | ||
550 | if (atomic_dec_and_test(&HFS_I(inode)->opencnt)) { | ||
551 | down(&inode->i_sem); | ||
552 | hfs_file_truncate(inode); | ||
553 | //if (inode->i_flags & S_DEAD) { | ||
554 | // hfs_delete_cat(inode->i_ino, HFSPLUS_SB(sb).hidden_dir, NULL); | ||
555 | // hfs_delete_inode(inode); | ||
556 | //} | ||
557 | up(&inode->i_sem); | ||
558 | } | ||
559 | return 0; | ||
560 | } | ||
561 | |||
562 | /* | ||
563 | * hfs_notify_change() | ||
564 | * | ||
565 | * Based very closely on fs/msdos/inode.c by Werner Almesberger | ||
566 | * | ||
567 | * This is the notify_change() field in the super_operations structure | ||
568 | * for HFS file systems. The purpose is to take that changes made to | ||
569 | * an inode and apply then in a filesystem-dependent manner. In this | ||
570 | * case the process has a few of tasks to do: | ||
571 | * 1) prevent changes to the i_uid and i_gid fields. | ||
572 | * 2) map file permissions to the closest allowable permissions | ||
573 | * 3) Since multiple Linux files can share the same on-disk inode under | ||
574 | * HFS (for instance the data and resource forks of a file) a change | ||
575 | * to permissions must be applied to all other in-core inodes which | ||
576 | * correspond to the same HFS file. | ||
577 | */ | ||
578 | |||
579 | int hfs_inode_setattr(struct dentry *dentry, struct iattr * attr) | ||
580 | { | ||
581 | struct inode *inode = dentry->d_inode; | ||
582 | struct hfs_sb_info *hsb = HFS_SB(inode->i_sb); | ||
583 | int error; | ||
584 | |||
585 | error = inode_change_ok(inode, attr); /* basic permission checks */ | ||
586 | if (error) | ||
587 | return error; | ||
588 | |||
589 | /* no uig/gid changes and limit which mode bits can be set */ | ||
590 | if (((attr->ia_valid & ATTR_UID) && | ||
591 | (attr->ia_uid != hsb->s_uid)) || | ||
592 | ((attr->ia_valid & ATTR_GID) && | ||
593 | (attr->ia_gid != hsb->s_gid)) || | ||
594 | ((attr->ia_valid & ATTR_MODE) && | ||
595 | ((S_ISDIR(inode->i_mode) && | ||
596 | (attr->ia_mode != inode->i_mode)) || | ||
597 | (attr->ia_mode & ~HFS_VALID_MODE_BITS)))) { | ||
598 | return hsb->s_quiet ? 0 : error; | ||
599 | } | ||
600 | |||
601 | if (attr->ia_valid & ATTR_MODE) { | ||
602 | /* Only the 'w' bits can ever change and only all together. */ | ||
603 | if (attr->ia_mode & S_IWUSR) | ||
604 | attr->ia_mode = inode->i_mode | S_IWUGO; | ||
605 | else | ||
606 | attr->ia_mode = inode->i_mode & ~S_IWUGO; | ||
607 | attr->ia_mode &= S_ISDIR(inode->i_mode) ? ~hsb->s_dir_umask: ~hsb->s_file_umask; | ||
608 | } | ||
609 | error = inode_setattr(inode, attr); | ||
610 | if (error) | ||
611 | return error; | ||
612 | |||
613 | return 0; | ||
614 | } | ||
615 | |||
616 | |||
617 | static struct file_operations hfs_file_operations = { | ||
618 | .llseek = generic_file_llseek, | ||
619 | .read = generic_file_read, | ||
620 | .write = generic_file_write, | ||
621 | .mmap = generic_file_mmap, | ||
622 | .sendfile = generic_file_sendfile, | ||
623 | .fsync = file_fsync, | ||
624 | .open = hfs_file_open, | ||
625 | .release = hfs_file_release, | ||
626 | }; | ||
627 | |||
628 | static struct inode_operations hfs_file_inode_operations = { | ||
629 | .lookup = hfs_file_lookup, | ||
630 | .truncate = hfs_file_truncate, | ||
631 | .setattr = hfs_inode_setattr, | ||
632 | .permission = hfs_permission, | ||
633 | .setxattr = hfs_setxattr, | ||
634 | .getxattr = hfs_getxattr, | ||
635 | .listxattr = hfs_listxattr, | ||
636 | }; | ||
diff --git a/fs/hfs/mdb.c b/fs/hfs/mdb.c new file mode 100644 index 000000000000..4efb640c4d0c --- /dev/null +++ b/fs/hfs/mdb.c | |||
@@ -0,0 +1,343 @@ | |||
1 | /* | ||
2 | * linux/fs/hfs/mdb.c | ||
3 | * | ||
4 | * Copyright (C) 1995-1997 Paul H. Hargrove | ||
5 | * (C) 2003 Ardis Technologies <roman@ardistech.com> | ||
6 | * This file may be distributed under the terms of the GNU General Public License. | ||
7 | * | ||
8 | * This file contains functions for reading/writing the MDB. | ||
9 | */ | ||
10 | |||
11 | #include <linux/cdrom.h> | ||
12 | #include <linux/genhd.h> | ||
13 | |||
14 | #include "hfs_fs.h" | ||
15 | #include "btree.h" | ||
16 | |||
17 | /*================ File-local data types ================*/ | ||
18 | |||
19 | /* | ||
20 | * The HFS Master Directory Block (MDB). | ||
21 | * | ||
22 | * Also known as the Volume Information Block (VIB), this structure is | ||
23 | * the HFS equivalent of a superblock. | ||
24 | * | ||
25 | * Reference: _Inside Macintosh: Files_ pages 2-59 through 2-62 | ||
26 | * | ||
27 | * modified for HFS Extended | ||
28 | */ | ||
29 | |||
30 | static int hfs_get_last_session(struct super_block *sb, | ||
31 | sector_t *start, sector_t *size) | ||
32 | { | ||
33 | struct cdrom_multisession ms_info; | ||
34 | struct cdrom_tocentry te; | ||
35 | int res; | ||
36 | |||
37 | /* default values */ | ||
38 | *start = 0; | ||
39 | *size = sb->s_bdev->bd_inode->i_size >> 9; | ||
40 | |||
41 | if (HFS_SB(sb)->session >= 0) { | ||
42 | te.cdte_track = HFS_SB(sb)->session; | ||
43 | te.cdte_format = CDROM_LBA; | ||
44 | res = ioctl_by_bdev(sb->s_bdev, CDROMREADTOCENTRY, (unsigned long)&te); | ||
45 | if (!res && (te.cdte_ctrl & CDROM_DATA_TRACK) == 4) { | ||
46 | *start = (sector_t)te.cdte_addr.lba << 2; | ||
47 | return 0; | ||
48 | } | ||
49 | printk(KERN_ERR "HFS: Invalid session number or type of track\n"); | ||
50 | return -EINVAL; | ||
51 | } | ||
52 | ms_info.addr_format = CDROM_LBA; | ||
53 | res = ioctl_by_bdev(sb->s_bdev, CDROMMULTISESSION, (unsigned long)&ms_info); | ||
54 | if (!res && ms_info.xa_flag) | ||
55 | *start = (sector_t)ms_info.addr.lba << 2; | ||
56 | return 0; | ||
57 | } | ||
58 | |||
59 | /* | ||
60 | * hfs_mdb_get() | ||
61 | * | ||
62 | * Build the in-core MDB for a filesystem, including | ||
63 | * the B-trees and the volume bitmap. | ||
64 | */ | ||
65 | int hfs_mdb_get(struct super_block *sb) | ||
66 | { | ||
67 | struct buffer_head *bh; | ||
68 | struct hfs_mdb *mdb, *mdb2; | ||
69 | unsigned int block; | ||
70 | char *ptr; | ||
71 | int off2, len, size, sect; | ||
72 | sector_t part_start, part_size; | ||
73 | loff_t off; | ||
74 | __be16 attrib; | ||
75 | |||
76 | /* set the device driver to 512-byte blocks */ | ||
77 | size = sb_min_blocksize(sb, HFS_SECTOR_SIZE); | ||
78 | if (!size) | ||
79 | return -EINVAL; | ||
80 | |||
81 | if (hfs_get_last_session(sb, &part_start, &part_size)) | ||
82 | return -EINVAL; | ||
83 | while (1) { | ||
84 | /* See if this is an HFS filesystem */ | ||
85 | bh = sb_bread512(sb, part_start + HFS_MDB_BLK, mdb); | ||
86 | if (!bh) | ||
87 | goto out; | ||
88 | |||
89 | if (mdb->drSigWord == cpu_to_be16(HFS_SUPER_MAGIC)) | ||
90 | break; | ||
91 | brelse(bh); | ||
92 | |||
93 | /* check for a partition block | ||
94 | * (should do this only for cdrom/loop though) | ||
95 | */ | ||
96 | if (hfs_part_find(sb, &part_start, &part_size)) | ||
97 | goto out; | ||
98 | } | ||
99 | |||
100 | HFS_SB(sb)->alloc_blksz = size = be32_to_cpu(mdb->drAlBlkSiz); | ||
101 | if (!size || (size & (HFS_SECTOR_SIZE - 1))) { | ||
102 | hfs_warn("hfs_fs: bad allocation block size %d\n", size); | ||
103 | goto out_bh; | ||
104 | } | ||
105 | |||
106 | size = min(HFS_SB(sb)->alloc_blksz, (u32)PAGE_SIZE); | ||
107 | /* size must be a multiple of 512 */ | ||
108 | while (size & (size - 1)) | ||
109 | size -= HFS_SECTOR_SIZE; | ||
110 | sect = be16_to_cpu(mdb->drAlBlSt) + part_start; | ||
111 | /* align block size to first sector */ | ||
112 | while (sect & ((size - 1) >> HFS_SECTOR_SIZE_BITS)) | ||
113 | size >>= 1; | ||
114 | /* align block size to weird alloc size */ | ||
115 | while (HFS_SB(sb)->alloc_blksz & (size - 1)) | ||
116 | size >>= 1; | ||
117 | brelse(bh); | ||
118 | if (!sb_set_blocksize(sb, size)) { | ||
119 | printk("hfs_fs: unable to set blocksize to %u\n", size); | ||
120 | goto out; | ||
121 | } | ||
122 | |||
123 | bh = sb_bread512(sb, part_start + HFS_MDB_BLK, mdb); | ||
124 | if (!bh) | ||
125 | goto out; | ||
126 | if (mdb->drSigWord != cpu_to_be16(HFS_SUPER_MAGIC)) | ||
127 | goto out_bh; | ||
128 | |||
129 | HFS_SB(sb)->mdb_bh = bh; | ||
130 | HFS_SB(sb)->mdb = mdb; | ||
131 | |||
132 | /* These parameters are read from the MDB, and never written */ | ||
133 | HFS_SB(sb)->part_start = part_start; | ||
134 | HFS_SB(sb)->fs_ablocks = be16_to_cpu(mdb->drNmAlBlks); | ||
135 | HFS_SB(sb)->fs_div = HFS_SB(sb)->alloc_blksz >> sb->s_blocksize_bits; | ||
136 | HFS_SB(sb)->clumpablks = be32_to_cpu(mdb->drClpSiz) / | ||
137 | HFS_SB(sb)->alloc_blksz; | ||
138 | if (!HFS_SB(sb)->clumpablks) | ||
139 | HFS_SB(sb)->clumpablks = 1; | ||
140 | HFS_SB(sb)->fs_start = (be16_to_cpu(mdb->drAlBlSt) + part_start) >> | ||
141 | (sb->s_blocksize_bits - HFS_SECTOR_SIZE_BITS); | ||
142 | |||
143 | /* These parameters are read from and written to the MDB */ | ||
144 | HFS_SB(sb)->free_ablocks = be16_to_cpu(mdb->drFreeBks); | ||
145 | HFS_SB(sb)->next_id = be32_to_cpu(mdb->drNxtCNID); | ||
146 | HFS_SB(sb)->root_files = be16_to_cpu(mdb->drNmFls); | ||
147 | HFS_SB(sb)->root_dirs = be16_to_cpu(mdb->drNmRtDirs); | ||
148 | HFS_SB(sb)->file_count = be32_to_cpu(mdb->drFilCnt); | ||
149 | HFS_SB(sb)->folder_count = be32_to_cpu(mdb->drDirCnt); | ||
150 | |||
151 | /* TRY to get the alternate (backup) MDB. */ | ||
152 | sect = part_start + part_size - 2; | ||
153 | bh = sb_bread512(sb, sect, mdb2); | ||
154 | if (bh) { | ||
155 | if (mdb2->drSigWord == cpu_to_be16(HFS_SUPER_MAGIC)) { | ||
156 | HFS_SB(sb)->alt_mdb_bh = bh; | ||
157 | HFS_SB(sb)->alt_mdb = mdb2; | ||
158 | } else | ||
159 | brelse(bh); | ||
160 | } | ||
161 | |||
162 | if (!HFS_SB(sb)->alt_mdb) { | ||
163 | hfs_warn("hfs_fs: unable to locate alternate MDB\n"); | ||
164 | hfs_warn("hfs_fs: continuing without an alternate MDB\n"); | ||
165 | } | ||
166 | |||
167 | HFS_SB(sb)->bitmap = (__be32 *)__get_free_pages(GFP_KERNEL, PAGE_SIZE < 8192 ? 1 : 0); | ||
168 | if (!HFS_SB(sb)->bitmap) | ||
169 | goto out; | ||
170 | |||
171 | /* read in the bitmap */ | ||
172 | block = be16_to_cpu(mdb->drVBMSt) + part_start; | ||
173 | off = (loff_t)block << HFS_SECTOR_SIZE_BITS; | ||
174 | size = (HFS_SB(sb)->fs_ablocks + 8) / 8; | ||
175 | ptr = (u8 *)HFS_SB(sb)->bitmap; | ||
176 | while (size) { | ||
177 | bh = sb_bread(sb, off >> sb->s_blocksize_bits); | ||
178 | if (!bh) { | ||
179 | hfs_warn("hfs_fs: unable to read volume bitmap\n"); | ||
180 | goto out; | ||
181 | } | ||
182 | off2 = off & (sb->s_blocksize - 1); | ||
183 | len = min((int)sb->s_blocksize - off2, size); | ||
184 | memcpy(ptr, bh->b_data + off2, len); | ||
185 | brelse(bh); | ||
186 | ptr += len; | ||
187 | off += len; | ||
188 | size -= len; | ||
189 | } | ||
190 | |||
191 | HFS_SB(sb)->ext_tree = hfs_btree_open(sb, HFS_EXT_CNID, hfs_ext_keycmp); | ||
192 | if (!HFS_SB(sb)->ext_tree) { | ||
193 | hfs_warn("hfs_fs: unable to open extent tree\n"); | ||
194 | goto out; | ||
195 | } | ||
196 | HFS_SB(sb)->cat_tree = hfs_btree_open(sb, HFS_CAT_CNID, hfs_cat_keycmp); | ||
197 | if (!HFS_SB(sb)->cat_tree) { | ||
198 | hfs_warn("hfs_fs: unable to open catalog tree\n"); | ||
199 | goto out; | ||
200 | } | ||
201 | |||
202 | attrib = mdb->drAtrb; | ||
203 | if (!(attrib & cpu_to_be16(HFS_SB_ATTRIB_UNMNT))) { | ||
204 | hfs_warn("HFS-fs warning: Filesystem was not cleanly unmounted, " | ||
205 | "running fsck.hfs is recommended. mounting read-only.\n"); | ||
206 | sb->s_flags |= MS_RDONLY; | ||
207 | } | ||
208 | if ((attrib & cpu_to_be16(HFS_SB_ATTRIB_SLOCK))) { | ||
209 | hfs_warn("HFS-fs: Filesystem is marked locked, mounting read-only.\n"); | ||
210 | sb->s_flags |= MS_RDONLY; | ||
211 | } | ||
212 | if (!(sb->s_flags & MS_RDONLY)) { | ||
213 | /* Mark the volume uncleanly unmounted in case we crash */ | ||
214 | attrib &= cpu_to_be16(~HFS_SB_ATTRIB_UNMNT); | ||
215 | attrib |= cpu_to_be16(HFS_SB_ATTRIB_INCNSTNT); | ||
216 | mdb->drAtrb = attrib; | ||
217 | mdb->drWrCnt = cpu_to_be32(be32_to_cpu(mdb->drWrCnt) + 1); | ||
218 | mdb->drLsMod = hfs_mtime(); | ||
219 | |||
220 | mark_buffer_dirty(HFS_SB(sb)->mdb_bh); | ||
221 | hfs_buffer_sync(HFS_SB(sb)->mdb_bh); | ||
222 | } | ||
223 | |||
224 | return 0; | ||
225 | |||
226 | out_bh: | ||
227 | brelse(bh); | ||
228 | out: | ||
229 | hfs_mdb_put(sb); | ||
230 | return -EIO; | ||
231 | } | ||
232 | |||
233 | /* | ||
234 | * hfs_mdb_commit() | ||
235 | * | ||
236 | * Description: | ||
237 | * This updates the MDB on disk (look also at hfs_write_super()). | ||
238 | * It does not check, if the superblock has been modified, or | ||
239 | * if the filesystem has been mounted read-only. It is mainly | ||
240 | * called by hfs_write_super() and hfs_btree_extend(). | ||
241 | * Input Variable(s): | ||
242 | * struct hfs_mdb *mdb: Pointer to the hfs MDB | ||
243 | * int backup; | ||
244 | * Output Variable(s): | ||
245 | * NONE | ||
246 | * Returns: | ||
247 | * void | ||
248 | * Preconditions: | ||
249 | * 'mdb' points to a "valid" (struct hfs_mdb). | ||
250 | * Postconditions: | ||
251 | * The HFS MDB and on disk will be updated, by copying the possibly | ||
252 | * modified fields from the in memory MDB (in native byte order) to | ||
253 | * the disk block buffer. | ||
254 | * If 'backup' is non-zero then the alternate MDB is also written | ||
255 | * and the function doesn't return until it is actually on disk. | ||
256 | */ | ||
257 | void hfs_mdb_commit(struct super_block *sb) | ||
258 | { | ||
259 | struct hfs_mdb *mdb = HFS_SB(sb)->mdb; | ||
260 | |||
261 | if (test_and_clear_bit(HFS_FLG_MDB_DIRTY, &HFS_SB(sb)->flags)) { | ||
262 | /* These parameters may have been modified, so write them back */ | ||
263 | mdb->drLsMod = hfs_mtime(); | ||
264 | mdb->drFreeBks = cpu_to_be16(HFS_SB(sb)->free_ablocks); | ||
265 | mdb->drNxtCNID = cpu_to_be32(HFS_SB(sb)->next_id); | ||
266 | mdb->drNmFls = cpu_to_be16(HFS_SB(sb)->root_files); | ||
267 | mdb->drNmRtDirs = cpu_to_be16(HFS_SB(sb)->root_dirs); | ||
268 | mdb->drFilCnt = cpu_to_be32(HFS_SB(sb)->file_count); | ||
269 | mdb->drDirCnt = cpu_to_be32(HFS_SB(sb)->folder_count); | ||
270 | |||
271 | /* write MDB to disk */ | ||
272 | mark_buffer_dirty(HFS_SB(sb)->mdb_bh); | ||
273 | } | ||
274 | |||
275 | /* write the backup MDB, not returning until it is written. | ||
276 | * we only do this when either the catalog or extents overflow | ||
277 | * files grow. */ | ||
278 | if (test_and_clear_bit(HFS_FLG_ALT_MDB_DIRTY, &HFS_SB(sb)->flags) && | ||
279 | HFS_SB(sb)->alt_mdb) { | ||
280 | hfs_inode_write_fork(HFS_SB(sb)->ext_tree->inode, mdb->drXTExtRec, | ||
281 | &mdb->drXTFlSize, NULL); | ||
282 | hfs_inode_write_fork(HFS_SB(sb)->cat_tree->inode, mdb->drCTExtRec, | ||
283 | &mdb->drCTFlSize, NULL); | ||
284 | memcpy(HFS_SB(sb)->alt_mdb, HFS_SB(sb)->mdb, HFS_SECTOR_SIZE); | ||
285 | HFS_SB(sb)->alt_mdb->drAtrb |= cpu_to_be16(HFS_SB_ATTRIB_UNMNT); | ||
286 | HFS_SB(sb)->alt_mdb->drAtrb &= cpu_to_be16(~HFS_SB_ATTRIB_INCNSTNT); | ||
287 | mark_buffer_dirty(HFS_SB(sb)->alt_mdb_bh); | ||
288 | hfs_buffer_sync(HFS_SB(sb)->alt_mdb_bh); | ||
289 | } | ||
290 | |||
291 | if (test_and_clear_bit(HFS_FLG_BITMAP_DIRTY, &HFS_SB(sb)->flags)) { | ||
292 | struct buffer_head *bh; | ||
293 | sector_t block; | ||
294 | char *ptr; | ||
295 | int off, size, len; | ||
296 | |||
297 | block = be16_to_cpu(HFS_SB(sb)->mdb->drVBMSt) + HFS_SB(sb)->part_start; | ||
298 | off = (block << HFS_SECTOR_SIZE_BITS) & (sb->s_blocksize - 1); | ||
299 | block >>= sb->s_blocksize_bits - HFS_SECTOR_SIZE_BITS; | ||
300 | size = (HFS_SB(sb)->fs_ablocks + 7) / 8; | ||
301 | ptr = (u8 *)HFS_SB(sb)->bitmap; | ||
302 | while (size) { | ||
303 | bh = sb_bread(sb, block); | ||
304 | if (!bh) { | ||
305 | hfs_warn("hfs_fs: unable to read volume bitmap\n"); | ||
306 | break; | ||
307 | } | ||
308 | len = min((int)sb->s_blocksize - off, size); | ||
309 | memcpy(bh->b_data + off, ptr, len); | ||
310 | mark_buffer_dirty(bh); | ||
311 | brelse(bh); | ||
312 | block++; | ||
313 | off = 0; | ||
314 | ptr += len; | ||
315 | size -= len; | ||
316 | } | ||
317 | } | ||
318 | } | ||
319 | |||
320 | void hfs_mdb_close(struct super_block *sb) | ||
321 | { | ||
322 | /* update volume attributes */ | ||
323 | if (sb->s_flags & MS_RDONLY) | ||
324 | return; | ||
325 | HFS_SB(sb)->mdb->drAtrb |= cpu_to_be16(HFS_SB_ATTRIB_UNMNT); | ||
326 | HFS_SB(sb)->mdb->drAtrb &= cpu_to_be16(~HFS_SB_ATTRIB_INCNSTNT); | ||
327 | mark_buffer_dirty(HFS_SB(sb)->mdb_bh); | ||
328 | } | ||
329 | |||
330 | /* | ||
331 | * hfs_mdb_put() | ||
332 | * | ||
333 | * Release the resources associated with the in-core MDB. */ | ||
334 | void hfs_mdb_put(struct super_block *sb) | ||
335 | { | ||
336 | /* free the B-trees */ | ||
337 | hfs_btree_close(HFS_SB(sb)->ext_tree); | ||
338 | hfs_btree_close(HFS_SB(sb)->cat_tree); | ||
339 | |||
340 | /* free the buffers holding the primary and alternate MDBs */ | ||
341 | brelse(HFS_SB(sb)->mdb_bh); | ||
342 | brelse(HFS_SB(sb)->alt_mdb_bh); | ||
343 | } | ||
diff --git a/fs/hfs/part_tbl.c b/fs/hfs/part_tbl.c new file mode 100644 index 000000000000..36add537d153 --- /dev/null +++ b/fs/hfs/part_tbl.c | |||
@@ -0,0 +1,117 @@ | |||
1 | /* | ||
2 | * linux/fs/hfs/part_tbl.c | ||
3 | * | ||
4 | * Copyright (C) 1996-1997 Paul H. Hargrove | ||
5 | * (C) 2003 Ardis Technologies <roman@ardistech.com> | ||
6 | * This file may be distributed under the terms of the GNU General Public License. | ||
7 | * | ||
8 | * Original code to handle the new style Mac partition table based on | ||
9 | * a patch contributed by Holger Schemel (aeglos@valinor.owl.de). | ||
10 | */ | ||
11 | |||
12 | #include "hfs_fs.h" | ||
13 | |||
14 | /* | ||
15 | * The new style Mac partition map | ||
16 | * | ||
17 | * For each partition on the media there is a physical block (512-byte | ||
18 | * block) containing one of these structures. These blocks are | ||
19 | * contiguous starting at block 1. | ||
20 | */ | ||
21 | struct new_pmap { | ||
22 | __be16 pmSig; /* signature */ | ||
23 | __be16 reSigPad; /* padding */ | ||
24 | __be32 pmMapBlkCnt; /* partition blocks count */ | ||
25 | __be32 pmPyPartStart; /* physical block start of partition */ | ||
26 | __be32 pmPartBlkCnt; /* physical block count of partition */ | ||
27 | u8 pmPartName[32]; /* (null terminated?) string | ||
28 | giving the name of this | ||
29 | partition */ | ||
30 | u8 pmPartType[32]; /* (null terminated?) string | ||
31 | giving the type of this | ||
32 | partition */ | ||
33 | /* a bunch more stuff we don't need */ | ||
34 | } __packed; | ||
35 | |||
36 | /* | ||
37 | * The old style Mac partition map | ||
38 | * | ||
39 | * The partition map consists for a 2-byte signature followed by an | ||
40 | * array of these structures. The map is terminated with an all-zero | ||
41 | * one of these. | ||
42 | */ | ||
43 | struct old_pmap { | ||
44 | __be16 pdSig; /* Signature bytes */ | ||
45 | struct old_pmap_entry { | ||
46 | __be32 pdStart; | ||
47 | __be32 pdSize; | ||
48 | __be32 pdFSID; | ||
49 | } pdEntry[42]; | ||
50 | } __packed; | ||
51 | |||
52 | /* | ||
53 | * hfs_part_find() | ||
54 | * | ||
55 | * Parse the partition map looking for the | ||
56 | * start and length of the 'part'th HFS partition. | ||
57 | */ | ||
58 | int hfs_part_find(struct super_block *sb, | ||
59 | sector_t *part_start, sector_t *part_size) | ||
60 | { | ||
61 | struct buffer_head *bh; | ||
62 | __be16 *data; | ||
63 | int i, size, res; | ||
64 | |||
65 | res = -ENOENT; | ||
66 | bh = sb_bread512(sb, *part_start + HFS_PMAP_BLK, data); | ||
67 | if (!bh) | ||
68 | return -EIO; | ||
69 | |||
70 | switch (be16_to_cpu(*data)) { | ||
71 | case HFS_OLD_PMAP_MAGIC: | ||
72 | { | ||
73 | struct old_pmap *pm; | ||
74 | struct old_pmap_entry *p; | ||
75 | |||
76 | pm = (struct old_pmap *)bh->b_data; | ||
77 | p = pm->pdEntry; | ||
78 | size = 42; | ||
79 | for (i = 0; i < size; p++, i++) { | ||
80 | if (p->pdStart && p->pdSize && | ||
81 | p->pdFSID == cpu_to_be32(0x54465331)/*"TFS1"*/ && | ||
82 | (HFS_SB(sb)->part < 0 || HFS_SB(sb)->part == i)) { | ||
83 | *part_start += be32_to_cpu(p->pdStart); | ||
84 | *part_size = be32_to_cpu(p->pdSize); | ||
85 | res = 0; | ||
86 | } | ||
87 | } | ||
88 | break; | ||
89 | } | ||
90 | case HFS_NEW_PMAP_MAGIC: | ||
91 | { | ||
92 | struct new_pmap *pm; | ||
93 | |||
94 | pm = (struct new_pmap *)bh->b_data; | ||
95 | size = be32_to_cpu(pm->pmMapBlkCnt); | ||
96 | for (i = 0; i < size;) { | ||
97 | if (!memcmp(pm->pmPartType,"Apple_HFS", 9) && | ||
98 | (HFS_SB(sb)->part < 0 || HFS_SB(sb)->part == i)) { | ||
99 | *part_start += be32_to_cpu(pm->pmPyPartStart); | ||
100 | *part_size = be32_to_cpu(pm->pmPartBlkCnt); | ||
101 | res = 0; | ||
102 | break; | ||
103 | } | ||
104 | brelse(bh); | ||
105 | bh = sb_bread512(sb, *part_start + HFS_PMAP_BLK + ++i, pm); | ||
106 | if (!bh) | ||
107 | return -EIO; | ||
108 | if (pm->pmSig != cpu_to_be16(HFS_NEW_PMAP_MAGIC)) | ||
109 | break; | ||
110 | } | ||
111 | break; | ||
112 | } | ||
113 | } | ||
114 | brelse(bh); | ||
115 | |||
116 | return res; | ||
117 | } | ||
diff --git a/fs/hfs/string.c b/fs/hfs/string.c new file mode 100644 index 000000000000..927a5af79428 --- /dev/null +++ b/fs/hfs/string.c | |||
@@ -0,0 +1,115 @@ | |||
1 | /* | ||
2 | * linux/fs/hfs/string.c | ||
3 | * | ||
4 | * Copyright (C) 1995-1997 Paul H. Hargrove | ||
5 | * (C) 2003 Ardis Technologies <roman@ardistech.com> | ||
6 | * This file may be distributed under the terms of the GNU General Public License. | ||
7 | * | ||
8 | * This file contains the string comparison function for the | ||
9 | * Macintosh character set. | ||
10 | * | ||
11 | * The code in this file is derived from code which is copyright | ||
12 | * 1986, 1989, 1990 by Abacus Research and Development, Inc. (ARDI) | ||
13 | * It is used here by the permission of ARDI's president Cliff Matthews. | ||
14 | */ | ||
15 | |||
16 | #include "hfs_fs.h" | ||
17 | #include <linux/dcache.h> | ||
18 | |||
19 | /*================ File-local variables ================*/ | ||
20 | |||
21 | /* | ||
22 | * unsigned char caseorder[] | ||
23 | * | ||
24 | * Defines the lexical ordering of characters on the Macintosh | ||
25 | * | ||
26 | * Composition of the 'casefold' and 'order' tables from ARDI's code | ||
27 | * with the entry for 0x20 changed to match that for 0xCA to remove | ||
28 | * special case for those two characters. | ||
29 | */ | ||
30 | static unsigned char caseorder[256] = { | ||
31 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F, | ||
32 | 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F, | ||
33 | 0x20,0x22,0x23,0x28,0x29,0x2A,0x2B,0x2C,0x2F,0x30,0x31,0x32,0x33,0x34,0x35,0x36, | ||
34 | 0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F,0x40,0x41,0x42,0x43,0x44,0x45,0x46, | ||
35 | 0x47,0x48,0x57,0x59,0x5D,0x5F,0x66,0x68,0x6A,0x6C,0x72,0x74,0x76,0x78,0x7A,0x7E, | ||
36 | 0x8C,0x8E,0x90,0x92,0x95,0x97,0x9E,0xA0,0xA2,0xA4,0xA7,0xA9,0xAA,0xAB,0xAC,0xAD, | ||
37 | 0x4E,0x48,0x57,0x59,0x5D,0x5F,0x66,0x68,0x6A,0x6C,0x72,0x74,0x76,0x78,0x7A,0x7E, | ||
38 | 0x8C,0x8E,0x90,0x92,0x95,0x97,0x9E,0xA0,0xA2,0xA4,0xA7,0xAF,0xB0,0xB1,0xB2,0xB3, | ||
39 | 0x4A,0x4C,0x5A,0x60,0x7B,0x7F,0x98,0x4F,0x49,0x51,0x4A,0x4B,0x4C,0x5A,0x60,0x63, | ||
40 | 0x64,0x65,0x6E,0x6F,0x70,0x71,0x7B,0x84,0x85,0x86,0x7F,0x80,0x9A,0x9B,0x9C,0x98, | ||
41 | 0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0x94,0xBB,0xBC,0xBD,0xBE,0xBF,0xC0,0x4D,0x81, | ||
42 | 0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0x55,0x8A,0xCC,0x4D,0x81, | ||
43 | 0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0x26,0x27,0xD4,0x20,0x49,0x4B,0x80,0x82,0x82, | ||
44 | 0xD5,0xD6,0x24,0x25,0x2D,0x2E,0xD7,0xD8,0xA6,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, | ||
45 | 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, | ||
46 | 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF | ||
47 | }; | ||
48 | |||
49 | /*================ Global functions ================*/ | ||
50 | |||
51 | /* | ||
52 | * Hash a string to an integer in a case-independent way | ||
53 | */ | ||
54 | int hfs_hash_dentry(struct dentry *dentry, struct qstr *this) | ||
55 | { | ||
56 | const unsigned char *name = this->name; | ||
57 | unsigned int hash, len = this->len; | ||
58 | |||
59 | if (len > HFS_NAMELEN) | ||
60 | len = HFS_NAMELEN; | ||
61 | |||
62 | hash = init_name_hash(); | ||
63 | for (; len; len--) | ||
64 | hash = partial_name_hash(caseorder[*name++], hash); | ||
65 | this->hash = end_name_hash(hash); | ||
66 | return 0; | ||
67 | } | ||
68 | |||
69 | /* | ||
70 | * Compare two strings in the HFS filename character ordering | ||
71 | * Returns positive, negative, or zero, not just 0 or (+/-)1 | ||
72 | * | ||
73 | * Equivalent to ARDI's call: | ||
74 | * ROMlib_RelString(s1+1, s2+1, true, false, (s1[0]<<16) | s2[0]) | ||
75 | */ | ||
76 | int hfs_strcmp(const unsigned char *s1, unsigned int len1, | ||
77 | const unsigned char *s2, unsigned int len2) | ||
78 | { | ||
79 | int len, tmp; | ||
80 | |||
81 | len = (len1 > len2) ? len2 : len1; | ||
82 | |||
83 | while (len--) { | ||
84 | tmp = (int)caseorder[*(s1++)] - (int)caseorder[*(s2++)]; | ||
85 | if (tmp) | ||
86 | return tmp; | ||
87 | } | ||
88 | return len1 - len2; | ||
89 | } | ||
90 | |||
91 | /* | ||
92 | * Test for equality of two strings in the HFS filename character ordering. | ||
93 | * return 1 on failure and 0 on success | ||
94 | */ | ||
95 | int hfs_compare_dentry(struct dentry *dentry, struct qstr *s1, struct qstr *s2) | ||
96 | { | ||
97 | const unsigned char *n1, *n2; | ||
98 | int len; | ||
99 | |||
100 | len = s1->len; | ||
101 | if (len >= HFS_NAMELEN) { | ||
102 | if (s2->len < HFS_NAMELEN) | ||
103 | return 1; | ||
104 | len = HFS_NAMELEN; | ||
105 | } else if (len != s2->len) | ||
106 | return 1; | ||
107 | |||
108 | n1 = s1->name; | ||
109 | n2 = s2->name; | ||
110 | while (len--) { | ||
111 | if (caseorder[*n1++] != caseorder[*n2++]) | ||
112 | return 1; | ||
113 | } | ||
114 | return 0; | ||
115 | } | ||
diff --git a/fs/hfs/super.c b/fs/hfs/super.c new file mode 100644 index 000000000000..1e2c193134cc --- /dev/null +++ b/fs/hfs/super.c | |||
@@ -0,0 +1,395 @@ | |||
1 | /* | ||
2 | * linux/fs/hfs/super.c | ||
3 | * | ||
4 | * Copyright (C) 1995-1997 Paul H. Hargrove | ||
5 | * (C) 2003 Ardis Technologies <roman@ardistech.com> | ||
6 | * This file may be distributed under the terms of the GNU General Public License. | ||
7 | * | ||
8 | * This file contains hfs_read_super(), some of the super_ops and | ||
9 | * init_module() and cleanup_module(). The remaining super_ops are in | ||
10 | * inode.c since they deal with inodes. | ||
11 | * | ||
12 | * Based on the minix file system code, (C) 1991, 1992 by Linus Torvalds | ||
13 | */ | ||
14 | |||
15 | #include <linux/config.h> | ||
16 | #include <linux/module.h> | ||
17 | #include <linux/blkdev.h> | ||
18 | #include <linux/init.h> | ||
19 | #include <linux/parser.h> | ||
20 | #include <linux/vfs.h> | ||
21 | |||
22 | #include "hfs_fs.h" | ||
23 | #include "btree.h" | ||
24 | |||
25 | static kmem_cache_t *hfs_inode_cachep; | ||
26 | |||
27 | MODULE_LICENSE("GPL"); | ||
28 | |||
29 | /* | ||
30 | * hfs_write_super() | ||
31 | * | ||
32 | * Description: | ||
33 | * This function is called by the VFS only. When the filesystem | ||
34 | * is mounted r/w it updates the MDB on disk. | ||
35 | * Input Variable(s): | ||
36 | * struct super_block *sb: Pointer to the hfs superblock | ||
37 | * Output Variable(s): | ||
38 | * NONE | ||
39 | * Returns: | ||
40 | * void | ||
41 | * Preconditions: | ||
42 | * 'sb' points to a "valid" (struct super_block). | ||
43 | * Postconditions: | ||
44 | * The MDB is marked 'unsuccessfully unmounted' by clearing bit 8 of drAtrb | ||
45 | * (hfs_put_super() must set this flag!). Some MDB fields are updated | ||
46 | * and the MDB buffer is written to disk by calling hfs_mdb_commit(). | ||
47 | */ | ||
48 | static void hfs_write_super(struct super_block *sb) | ||
49 | { | ||
50 | sb->s_dirt = 0; | ||
51 | if (sb->s_flags & MS_RDONLY) | ||
52 | return; | ||
53 | /* sync everything to the buffers */ | ||
54 | hfs_mdb_commit(sb); | ||
55 | } | ||
56 | |||
57 | /* | ||
58 | * hfs_put_super() | ||
59 | * | ||
60 | * This is the put_super() entry in the super_operations structure for | ||
61 | * HFS filesystems. The purpose is to release the resources | ||
62 | * associated with the superblock sb. | ||
63 | */ | ||
64 | static void hfs_put_super(struct super_block *sb) | ||
65 | { | ||
66 | hfs_mdb_close(sb); | ||
67 | /* release the MDB's resources */ | ||
68 | hfs_mdb_put(sb); | ||
69 | } | ||
70 | |||
71 | /* | ||
72 | * hfs_statfs() | ||
73 | * | ||
74 | * This is the statfs() entry in the super_operations structure for | ||
75 | * HFS filesystems. The purpose is to return various data about the | ||
76 | * filesystem. | ||
77 | * | ||
78 | * changed f_files/f_ffree to reflect the fs_ablock/free_ablocks. | ||
79 | */ | ||
80 | static int hfs_statfs(struct super_block *sb, struct kstatfs *buf) | ||
81 | { | ||
82 | buf->f_type = HFS_SUPER_MAGIC; | ||
83 | buf->f_bsize = sb->s_blocksize; | ||
84 | buf->f_blocks = (u32)HFS_SB(sb)->fs_ablocks * HFS_SB(sb)->fs_div; | ||
85 | buf->f_bfree = (u32)HFS_SB(sb)->free_ablocks * HFS_SB(sb)->fs_div; | ||
86 | buf->f_bavail = buf->f_bfree; | ||
87 | buf->f_files = HFS_SB(sb)->fs_ablocks; | ||
88 | buf->f_ffree = HFS_SB(sb)->free_ablocks; | ||
89 | buf->f_namelen = HFS_NAMELEN; | ||
90 | |||
91 | return 0; | ||
92 | } | ||
93 | |||
94 | static int hfs_remount(struct super_block *sb, int *flags, char *data) | ||
95 | { | ||
96 | *flags |= MS_NODIRATIME; | ||
97 | if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY)) | ||
98 | return 0; | ||
99 | if (!(*flags & MS_RDONLY)) { | ||
100 | if (!(HFS_SB(sb)->mdb->drAtrb & cpu_to_be16(HFS_SB_ATTRIB_UNMNT))) { | ||
101 | printk("HFS-fs warning: Filesystem was not cleanly unmounted, " | ||
102 | "running fsck.hfs is recommended. leaving read-only.\n"); | ||
103 | sb->s_flags |= MS_RDONLY; | ||
104 | *flags |= MS_RDONLY; | ||
105 | } else if (HFS_SB(sb)->mdb->drAtrb & cpu_to_be16(HFS_SB_ATTRIB_SLOCK)) { | ||
106 | printk("HFS-fs: Filesystem is marked locked, leaving read-only.\n"); | ||
107 | sb->s_flags |= MS_RDONLY; | ||
108 | *flags |= MS_RDONLY; | ||
109 | } | ||
110 | } | ||
111 | return 0; | ||
112 | } | ||
113 | |||
114 | static struct inode *hfs_alloc_inode(struct super_block *sb) | ||
115 | { | ||
116 | struct hfs_inode_info *i; | ||
117 | |||
118 | i = kmem_cache_alloc(hfs_inode_cachep, SLAB_KERNEL); | ||
119 | return i ? &i->vfs_inode : NULL; | ||
120 | } | ||
121 | |||
122 | static void hfs_destroy_inode(struct inode *inode) | ||
123 | { | ||
124 | kmem_cache_free(hfs_inode_cachep, HFS_I(inode)); | ||
125 | } | ||
126 | |||
127 | static struct super_operations hfs_super_operations = { | ||
128 | .alloc_inode = hfs_alloc_inode, | ||
129 | .destroy_inode = hfs_destroy_inode, | ||
130 | .write_inode = hfs_write_inode, | ||
131 | .clear_inode = hfs_clear_inode, | ||
132 | .put_super = hfs_put_super, | ||
133 | .write_super = hfs_write_super, | ||
134 | .statfs = hfs_statfs, | ||
135 | .remount_fs = hfs_remount, | ||
136 | }; | ||
137 | |||
138 | enum { | ||
139 | opt_uid, opt_gid, opt_umask, opt_file_umask, opt_dir_umask, | ||
140 | opt_part, opt_session, opt_type, opt_creator, opt_quiet, | ||
141 | opt_err | ||
142 | }; | ||
143 | |||
144 | static match_table_t tokens = { | ||
145 | { opt_uid, "uid=%u" }, | ||
146 | { opt_gid, "gid=%u" }, | ||
147 | { opt_umask, "umask=%o" }, | ||
148 | { opt_file_umask, "file_umask=%o" }, | ||
149 | { opt_dir_umask, "dir_umask=%o" }, | ||
150 | { opt_part, "part=%u" }, | ||
151 | { opt_session, "session=%u" }, | ||
152 | { opt_type, "type=%s" }, | ||
153 | { opt_creator, "creator=%s" }, | ||
154 | { opt_quiet, "quiet" }, | ||
155 | { opt_err, NULL } | ||
156 | }; | ||
157 | |||
158 | static inline int match_fourchar(substring_t *arg, u32 *result) | ||
159 | { | ||
160 | if (arg->to - arg->from != 4) | ||
161 | return -EINVAL; | ||
162 | memcpy(result, arg->from, 4); | ||
163 | return 0; | ||
164 | } | ||
165 | |||
166 | /* | ||
167 | * parse_options() | ||
168 | * | ||
169 | * adapted from linux/fs/msdos/inode.c written 1992,93 by Werner Almesberger | ||
170 | * This function is called by hfs_read_super() to parse the mount options. | ||
171 | */ | ||
172 | static int parse_options(char *options, struct hfs_sb_info *hsb) | ||
173 | { | ||
174 | char *p; | ||
175 | substring_t args[MAX_OPT_ARGS]; | ||
176 | int tmp, token; | ||
177 | |||
178 | /* initialize the sb with defaults */ | ||
179 | hsb->s_uid = current->uid; | ||
180 | hsb->s_gid = current->gid; | ||
181 | hsb->s_file_umask = 0133; | ||
182 | hsb->s_dir_umask = 0022; | ||
183 | hsb->s_type = hsb->s_creator = cpu_to_be32(0x3f3f3f3f); /* == '????' */ | ||
184 | hsb->s_quiet = 0; | ||
185 | hsb->part = -1; | ||
186 | hsb->session = -1; | ||
187 | |||
188 | if (!options) | ||
189 | return 1; | ||
190 | |||
191 | while ((p = strsep(&options, ",")) != NULL) { | ||
192 | if (!*p) | ||
193 | continue; | ||
194 | |||
195 | token = match_token(p, tokens, args); | ||
196 | switch (token) { | ||
197 | case opt_uid: | ||
198 | if (match_int(&args[0], &tmp)) { | ||
199 | printk("HFS: uid requires an argument\n"); | ||
200 | return 0; | ||
201 | } | ||
202 | hsb->s_uid = (uid_t)tmp; | ||
203 | break; | ||
204 | case opt_gid: | ||
205 | if (match_int(&args[0], &tmp)) { | ||
206 | printk("HFS: gid requires an argument\n"); | ||
207 | return 0; | ||
208 | } | ||
209 | hsb->s_gid = (gid_t)tmp; | ||
210 | break; | ||
211 | case opt_umask: | ||
212 | if (match_octal(&args[0], &tmp)) { | ||
213 | printk("HFS: umask requires a value\n"); | ||
214 | return 0; | ||
215 | } | ||
216 | hsb->s_file_umask = (umode_t)tmp; | ||
217 | hsb->s_dir_umask = (umode_t)tmp; | ||
218 | break; | ||
219 | case opt_file_umask: | ||
220 | if (match_octal(&args[0], &tmp)) { | ||
221 | printk("HFS: file_umask requires a value\n"); | ||
222 | return 0; | ||
223 | } | ||
224 | hsb->s_file_umask = (umode_t)tmp; | ||
225 | break; | ||
226 | case opt_dir_umask: | ||
227 | if (match_octal(&args[0], &tmp)) { | ||
228 | printk("HFS: dir_umask requires a value\n"); | ||
229 | return 0; | ||
230 | } | ||
231 | hsb->s_dir_umask = (umode_t)tmp; | ||
232 | break; | ||
233 | case opt_part: | ||
234 | if (match_int(&args[0], &hsb->part)) { | ||
235 | printk("HFS: part requires an argument\n"); | ||
236 | return 0; | ||
237 | } | ||
238 | break; | ||
239 | case opt_session: | ||
240 | if (match_int(&args[0], &hsb->session)) { | ||
241 | printk("HFS: session requires an argument\n"); | ||
242 | return 0; | ||
243 | } | ||
244 | break; | ||
245 | case opt_type: | ||
246 | if (match_fourchar(&args[0], &hsb->s_type)) { | ||
247 | printk("HFS+-fs: type requires a 4 character value\n"); | ||
248 | return 0; | ||
249 | } | ||
250 | break; | ||
251 | case opt_creator: | ||
252 | if (match_fourchar(&args[0], &hsb->s_creator)) { | ||
253 | printk("HFS+-fs: creator requires a 4 character value\n"); | ||
254 | return 0; | ||
255 | } | ||
256 | break; | ||
257 | case opt_quiet: | ||
258 | hsb->s_quiet = 1; | ||
259 | break; | ||
260 | default: | ||
261 | return 0; | ||
262 | } | ||
263 | } | ||
264 | |||
265 | hsb->s_dir_umask &= 0777; | ||
266 | hsb->s_file_umask &= 0577; | ||
267 | |||
268 | return 1; | ||
269 | } | ||
270 | |||
271 | /* | ||
272 | * hfs_read_super() | ||
273 | * | ||
274 | * This is the function that is responsible for mounting an HFS | ||
275 | * filesystem. It performs all the tasks necessary to get enough data | ||
276 | * from the disk to read the root inode. This includes parsing the | ||
277 | * mount options, dealing with Macintosh partitions, reading the | ||
278 | * superblock and the allocation bitmap blocks, calling | ||
279 | * hfs_btree_init() to get the necessary data about the extents and | ||
280 | * catalog B-trees and, finally, reading the root inode into memory. | ||
281 | */ | ||
282 | static int hfs_fill_super(struct super_block *sb, void *data, int silent) | ||
283 | { | ||
284 | struct hfs_sb_info *sbi; | ||
285 | struct hfs_find_data fd; | ||
286 | hfs_cat_rec rec; | ||
287 | struct inode *root_inode; | ||
288 | int res; | ||
289 | |||
290 | sbi = kmalloc(sizeof(struct hfs_sb_info), GFP_KERNEL); | ||
291 | if (!sbi) | ||
292 | return -ENOMEM; | ||
293 | sb->s_fs_info = sbi; | ||
294 | memset(sbi, 0, sizeof(struct hfs_sb_info)); | ||
295 | INIT_HLIST_HEAD(&sbi->rsrc_inodes); | ||
296 | |||
297 | res = -EINVAL; | ||
298 | if (!parse_options((char *)data, sbi)) { | ||
299 | hfs_warn("hfs_fs: unable to parse mount options.\n"); | ||
300 | goto bail3; | ||
301 | } | ||
302 | |||
303 | sb->s_op = &hfs_super_operations; | ||
304 | sb->s_flags |= MS_NODIRATIME; | ||
305 | init_MUTEX(&sbi->bitmap_lock); | ||
306 | |||
307 | res = hfs_mdb_get(sb); | ||
308 | if (res) { | ||
309 | if (!silent) | ||
310 | hfs_warn("VFS: Can't find a HFS filesystem on dev %s.\n", | ||
311 | hfs_mdb_name(sb)); | ||
312 | res = -EINVAL; | ||
313 | goto bail2; | ||
314 | } | ||
315 | |||
316 | /* try to get the root inode */ | ||
317 | hfs_find_init(HFS_SB(sb)->cat_tree, &fd); | ||
318 | res = hfs_cat_find_brec(sb, HFS_ROOT_CNID, &fd); | ||
319 | if (!res) | ||
320 | hfs_bnode_read(fd.bnode, &rec, fd.entryoffset, fd.entrylength); | ||
321 | if (res) { | ||
322 | hfs_find_exit(&fd); | ||
323 | goto bail_no_root; | ||
324 | } | ||
325 | root_inode = hfs_iget(sb, &fd.search_key->cat, &rec); | ||
326 | hfs_find_exit(&fd); | ||
327 | if (!root_inode) | ||
328 | goto bail_no_root; | ||
329 | |||
330 | sb->s_root = d_alloc_root(root_inode); | ||
331 | if (!sb->s_root) | ||
332 | goto bail_iput; | ||
333 | |||
334 | sb->s_root->d_op = &hfs_dentry_operations; | ||
335 | |||
336 | /* everything's okay */ | ||
337 | return 0; | ||
338 | |||
339 | bail_iput: | ||
340 | iput(root_inode); | ||
341 | bail_no_root: | ||
342 | hfs_warn("hfs_fs: get root inode failed.\n"); | ||
343 | hfs_mdb_put(sb); | ||
344 | bail2: | ||
345 | bail3: | ||
346 | kfree(sbi); | ||
347 | return res; | ||
348 | } | ||
349 | |||
350 | static struct super_block *hfs_get_sb(struct file_system_type *fs_type, | ||
351 | int flags, const char *dev_name, void *data) | ||
352 | { | ||
353 | return get_sb_bdev(fs_type, flags, dev_name, data, hfs_fill_super); | ||
354 | } | ||
355 | |||
356 | static struct file_system_type hfs_fs_type = { | ||
357 | .owner = THIS_MODULE, | ||
358 | .name = "hfs", | ||
359 | .get_sb = hfs_get_sb, | ||
360 | .kill_sb = kill_block_super, | ||
361 | .fs_flags = FS_REQUIRES_DEV, | ||
362 | }; | ||
363 | |||
364 | static void hfs_init_once(void *p, kmem_cache_t *cachep, unsigned long flags) | ||
365 | { | ||
366 | struct hfs_inode_info *i = p; | ||
367 | |||
368 | if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == SLAB_CTOR_CONSTRUCTOR) | ||
369 | inode_init_once(&i->vfs_inode); | ||
370 | } | ||
371 | |||
372 | static int __init init_hfs_fs(void) | ||
373 | { | ||
374 | int err; | ||
375 | |||
376 | hfs_inode_cachep = kmem_cache_create("hfs_inode_cache", | ||
377 | sizeof(struct hfs_inode_info), 0, SLAB_HWCACHE_ALIGN, | ||
378 | hfs_init_once, NULL); | ||
379 | if (!hfs_inode_cachep) | ||
380 | return -ENOMEM; | ||
381 | err = register_filesystem(&hfs_fs_type); | ||
382 | if (err) | ||
383 | kmem_cache_destroy(hfs_inode_cachep); | ||
384 | return err; | ||
385 | } | ||
386 | |||
387 | static void __exit exit_hfs_fs(void) | ||
388 | { | ||
389 | unregister_filesystem(&hfs_fs_type); | ||
390 | if (kmem_cache_destroy(hfs_inode_cachep)) | ||
391 | printk(KERN_INFO "hfs_inode_cache: not all structures were freed\n"); | ||
392 | } | ||
393 | |||
394 | module_init(init_hfs_fs) | ||
395 | module_exit(exit_hfs_fs) | ||
diff --git a/fs/hfs/sysdep.c b/fs/hfs/sysdep.c new file mode 100644 index 000000000000..5bf89ec01cd4 --- /dev/null +++ b/fs/hfs/sysdep.c | |||
@@ -0,0 +1,40 @@ | |||
1 | /* | ||
2 | * linux/fs/hfs/sysdep.c | ||
3 | * | ||
4 | * Copyright (C) 1996 Paul H. Hargrove | ||
5 | * (C) 2003 Ardis Technologies <roman@ardistech.com> | ||
6 | * This file may be distributed under the terms of the GNU General Public License. | ||
7 | * | ||
8 | * This file contains the code to do various system dependent things. | ||
9 | */ | ||
10 | |||
11 | #include "hfs_fs.h" | ||
12 | |||
13 | /* dentry case-handling: just lowercase everything */ | ||
14 | |||
15 | static int hfs_revalidate_dentry(struct dentry *dentry, struct nameidata *nd) | ||
16 | { | ||
17 | struct inode *inode = dentry->d_inode; | ||
18 | int diff; | ||
19 | |||
20 | if(!inode) | ||
21 | return 1; | ||
22 | |||
23 | /* fix up inode on a timezone change */ | ||
24 | diff = sys_tz.tz_minuteswest * 60 - HFS_I(inode)->tz_secondswest; | ||
25 | if (diff) { | ||
26 | inode->i_ctime.tv_sec += diff; | ||
27 | inode->i_atime.tv_sec += diff; | ||
28 | inode->i_mtime.tv_sec += diff; | ||
29 | HFS_I(inode)->tz_secondswest += diff; | ||
30 | } | ||
31 | return 1; | ||
32 | } | ||
33 | |||
34 | struct dentry_operations hfs_dentry_operations = | ||
35 | { | ||
36 | .d_revalidate = hfs_revalidate_dentry, | ||
37 | .d_hash = hfs_hash_dentry, | ||
38 | .d_compare = hfs_compare_dentry, | ||
39 | }; | ||
40 | |||
diff --git a/fs/hfs/trans.c b/fs/hfs/trans.c new file mode 100644 index 000000000000..fb9720abbadd --- /dev/null +++ b/fs/hfs/trans.c | |||
@@ -0,0 +1,72 @@ | |||
1 | /* | ||
2 | * linux/fs/hfs/trans.c | ||
3 | * | ||
4 | * Copyright (C) 1995-1997 Paul H. Hargrove | ||
5 | * This file may be distributed under the terms of the GNU General Public License. | ||
6 | * | ||
7 | * This file contains routines for converting between the Macintosh | ||
8 | * character set and various other encodings. This includes dealing | ||
9 | * with ':' vs. '/' as the path-element separator. | ||
10 | */ | ||
11 | |||
12 | #include "hfs_fs.h" | ||
13 | |||
14 | /*================ Global functions ================*/ | ||
15 | |||
16 | /* | ||
17 | * hfs_mac2triv() | ||
18 | * | ||
19 | * Given a 'Pascal String' (a string preceded by a length byte) in | ||
20 | * the Macintosh character set produce the corresponding filename using | ||
21 | * the 'trivial' name-mangling scheme, returning the length of the | ||
22 | * mangled filename. Note that the output string is not NULL | ||
23 | * terminated. | ||
24 | * | ||
25 | * The name-mangling works as follows: | ||
26 | * The character '/', which is illegal in Linux filenames is replaced | ||
27 | * by ':' which never appears in HFS filenames. All other characters | ||
28 | * are passed unchanged from input to output. | ||
29 | */ | ||
30 | int hfs_mac2triv(char *out, const struct hfs_name *in) | ||
31 | { | ||
32 | const char *p; | ||
33 | char c; | ||
34 | int i, len; | ||
35 | |||
36 | len = in->len; | ||
37 | p = in->name; | ||
38 | for (i = 0; i < len; i++) { | ||
39 | c = *p++; | ||
40 | *out++ = c == '/' ? ':' : c; | ||
41 | } | ||
42 | return i; | ||
43 | } | ||
44 | |||
45 | /* | ||
46 | * hfs_triv2mac() | ||
47 | * | ||
48 | * Given an ASCII string (not null-terminated) and its length, | ||
49 | * generate the corresponding filename in the Macintosh character set | ||
50 | * using the 'trivial' name-mangling scheme, returning the length of | ||
51 | * the mangled filename. Note that the output string is not NULL | ||
52 | * terminated. | ||
53 | * | ||
54 | * This routine is a inverse to hfs_mac2triv(). | ||
55 | * A ':' is replaced by a '/'. | ||
56 | */ | ||
57 | void hfs_triv2mac(struct hfs_name *out, struct qstr *in) | ||
58 | { | ||
59 | const char *src; | ||
60 | char *dst, c; | ||
61 | int i, len; | ||
62 | |||
63 | out->len = len = min((unsigned int)HFS_NAMELEN, in->len); | ||
64 | src = in->name; | ||
65 | dst = out->name; | ||
66 | for (i = 0; i < len; i++) { | ||
67 | c = *src++; | ||
68 | *dst++ = c == ':' ? '/' : c; | ||
69 | } | ||
70 | for (; i < HFS_NAMELEN; i++) | ||
71 | *dst++ = 0; | ||
72 | } | ||