1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * super.c |
4 | * |
5 | * Copyright (c) 1999 Al Smith |
6 | * |
7 | * Portions derived from work (c) 1995,1996 Christian Vogelgsang. |
8 | */ |
9 | |
10 | #include <linux/init.h> |
11 | #include <linux/module.h> |
12 | #include <linux/exportfs.h> |
13 | #include <linux/slab.h> |
14 | #include <linux/buffer_head.h> |
15 | #include <linux/vfs.h> |
16 | #include <linux/blkdev.h> |
17 | |
18 | #include "efs.h" |
19 | #include <linux/efs_vh.h> |
20 | #include <linux/efs_fs_sb.h> |
21 | |
22 | static int efs_statfs(struct dentry *dentry, struct kstatfs *buf); |
23 | static int efs_fill_super(struct super_block *s, void *d, int silent); |
24 | |
25 | static struct dentry *efs_mount(struct file_system_type *fs_type, |
26 | int flags, const char *dev_name, void *data) |
27 | { |
28 | return mount_bdev(fs_type, flags, dev_name, data, fill_super: efs_fill_super); |
29 | } |
30 | |
31 | static void efs_kill_sb(struct super_block *s) |
32 | { |
33 | struct efs_sb_info *sbi = SUPER_INFO(sb: s); |
34 | kill_block_super(sb: s); |
35 | kfree(objp: sbi); |
36 | } |
37 | |
38 | static struct file_system_type efs_fs_type = { |
39 | .owner = THIS_MODULE, |
40 | .name = "efs" , |
41 | .mount = efs_mount, |
42 | .kill_sb = efs_kill_sb, |
43 | .fs_flags = FS_REQUIRES_DEV, |
44 | }; |
45 | MODULE_ALIAS_FS("efs" ); |
46 | |
47 | static struct pt_types sgi_pt_types[] = { |
48 | {0x00, "SGI vh" }, |
49 | {0x01, "SGI trkrepl" }, |
50 | {0x02, "SGI secrepl" }, |
51 | {0x03, "SGI raw" }, |
52 | {0x04, "SGI bsd" }, |
53 | {SGI_SYSV, "SGI sysv" }, |
54 | {0x06, "SGI vol" }, |
55 | {SGI_EFS, "SGI efs" }, |
56 | {0x08, "SGI lv" }, |
57 | {0x09, "SGI rlv" }, |
58 | {0x0A, "SGI xfs" }, |
59 | {0x0B, "SGI xfslog" }, |
60 | {0x0C, "SGI xlv" }, |
61 | {0x82, "Linux swap" }, |
62 | {0x83, "Linux native" }, |
63 | {0, NULL} |
64 | }; |
65 | |
66 | |
67 | static struct kmem_cache * efs_inode_cachep; |
68 | |
69 | static struct inode *efs_alloc_inode(struct super_block *sb) |
70 | { |
71 | struct efs_inode_info *ei; |
72 | ei = alloc_inode_sb(sb, cache: efs_inode_cachep, GFP_KERNEL); |
73 | if (!ei) |
74 | return NULL; |
75 | return &ei->vfs_inode; |
76 | } |
77 | |
78 | static void efs_free_inode(struct inode *inode) |
79 | { |
80 | kmem_cache_free(s: efs_inode_cachep, objp: INODE_INFO(inode)); |
81 | } |
82 | |
83 | static void init_once(void *foo) |
84 | { |
85 | struct efs_inode_info *ei = (struct efs_inode_info *) foo; |
86 | |
87 | inode_init_once(&ei->vfs_inode); |
88 | } |
89 | |
90 | static int __init init_inodecache(void) |
91 | { |
92 | efs_inode_cachep = kmem_cache_create(name: "efs_inode_cache" , |
93 | size: sizeof(struct efs_inode_info), align: 0, |
94 | SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD| |
95 | SLAB_ACCOUNT, ctor: init_once); |
96 | if (efs_inode_cachep == NULL) |
97 | return -ENOMEM; |
98 | return 0; |
99 | } |
100 | |
101 | static void destroy_inodecache(void) |
102 | { |
103 | /* |
104 | * Make sure all delayed rcu free inodes are flushed before we |
105 | * destroy cache. |
106 | */ |
107 | rcu_barrier(); |
108 | kmem_cache_destroy(s: efs_inode_cachep); |
109 | } |
110 | |
111 | static int efs_remount(struct super_block *sb, int *flags, char *data) |
112 | { |
113 | sync_filesystem(sb); |
114 | *flags |= SB_RDONLY; |
115 | return 0; |
116 | } |
117 | |
118 | static const struct super_operations efs_superblock_operations = { |
119 | .alloc_inode = efs_alloc_inode, |
120 | .free_inode = efs_free_inode, |
121 | .statfs = efs_statfs, |
122 | .remount_fs = efs_remount, |
123 | }; |
124 | |
125 | static const struct export_operations efs_export_ops = { |
126 | .encode_fh = generic_encode_ino32_fh, |
127 | .fh_to_dentry = efs_fh_to_dentry, |
128 | .fh_to_parent = efs_fh_to_parent, |
129 | .get_parent = efs_get_parent, |
130 | }; |
131 | |
132 | static int __init init_efs_fs(void) { |
133 | int err; |
134 | pr_info(EFS_VERSION" - http://aeschi.ch.eu.org/efs/\n" ); |
135 | err = init_inodecache(); |
136 | if (err) |
137 | goto out1; |
138 | err = register_filesystem(&efs_fs_type); |
139 | if (err) |
140 | goto out; |
141 | return 0; |
142 | out: |
143 | destroy_inodecache(); |
144 | out1: |
145 | return err; |
146 | } |
147 | |
148 | static void __exit exit_efs_fs(void) { |
149 | unregister_filesystem(&efs_fs_type); |
150 | destroy_inodecache(); |
151 | } |
152 | |
153 | module_init(init_efs_fs) |
154 | module_exit(exit_efs_fs) |
155 | |
156 | static efs_block_t efs_validate_vh(struct volume_header *vh) { |
157 | int i; |
158 | __be32 cs, *ui; |
159 | int csum; |
160 | efs_block_t sblock = 0; /* shuts up gcc */ |
161 | struct pt_types *pt_entry; |
162 | int pt_type, slice = -1; |
163 | |
164 | if (be32_to_cpu(vh->vh_magic) != VHMAGIC) { |
165 | /* |
166 | * assume that we're dealing with a partition and allow |
167 | * read_super() to try and detect a valid superblock |
168 | * on the next block. |
169 | */ |
170 | return 0; |
171 | } |
172 | |
173 | ui = ((__be32 *) (vh + 1)) - 1; |
174 | for(csum = 0; ui >= ((__be32 *) vh);) { |
175 | cs = *ui--; |
176 | csum += be32_to_cpu(cs); |
177 | } |
178 | if (csum) { |
179 | pr_warn("SGI disklabel: checksum bad, label corrupted\n" ); |
180 | return 0; |
181 | } |
182 | |
183 | #ifdef DEBUG |
184 | pr_debug("bf: \"%16s\"\n" , vh->vh_bootfile); |
185 | |
186 | for(i = 0; i < NVDIR; i++) { |
187 | int j; |
188 | char name[VDNAMESIZE+1]; |
189 | |
190 | for(j = 0; j < VDNAMESIZE; j++) { |
191 | name[j] = vh->vh_vd[i].vd_name[j]; |
192 | } |
193 | name[j] = (char) 0; |
194 | |
195 | if (name[0]) { |
196 | pr_debug("vh: %8s block: 0x%08x size: 0x%08x\n" , |
197 | name, (int) be32_to_cpu(vh->vh_vd[i].vd_lbn), |
198 | (int) be32_to_cpu(vh->vh_vd[i].vd_nbytes)); |
199 | } |
200 | } |
201 | #endif |
202 | |
203 | for(i = 0; i < NPARTAB; i++) { |
204 | pt_type = (int) be32_to_cpu(vh->vh_pt[i].pt_type); |
205 | for(pt_entry = sgi_pt_types; pt_entry->pt_name; pt_entry++) { |
206 | if (pt_type == pt_entry->pt_type) break; |
207 | } |
208 | #ifdef DEBUG |
209 | if (be32_to_cpu(vh->vh_pt[i].pt_nblks)) { |
210 | pr_debug("pt %2d: start: %08d size: %08d type: 0x%02x (%s)\n" , |
211 | i, (int)be32_to_cpu(vh->vh_pt[i].pt_firstlbn), |
212 | (int)be32_to_cpu(vh->vh_pt[i].pt_nblks), |
213 | pt_type, (pt_entry->pt_name) ? |
214 | pt_entry->pt_name : "unknown" ); |
215 | } |
216 | #endif |
217 | if (IS_EFS(pt_type)) { |
218 | sblock = be32_to_cpu(vh->vh_pt[i].pt_firstlbn); |
219 | slice = i; |
220 | } |
221 | } |
222 | |
223 | if (slice == -1) { |
224 | pr_notice("partition table contained no EFS partitions\n" ); |
225 | #ifdef DEBUG |
226 | } else { |
227 | pr_info("using slice %d (type %s, offset 0x%x)\n" , slice, |
228 | (pt_entry->pt_name) ? pt_entry->pt_name : "unknown" , |
229 | sblock); |
230 | #endif |
231 | } |
232 | return sblock; |
233 | } |
234 | |
235 | static int efs_validate_super(struct efs_sb_info *sb, struct efs_super *super) { |
236 | |
237 | if (!IS_EFS_MAGIC(be32_to_cpu(super->fs_magic))) |
238 | return -1; |
239 | |
240 | sb->fs_magic = be32_to_cpu(super->fs_magic); |
241 | sb->total_blocks = be32_to_cpu(super->fs_size); |
242 | sb->first_block = be32_to_cpu(super->fs_firstcg); |
243 | sb->group_size = be32_to_cpu(super->fs_cgfsize); |
244 | sb->data_free = be32_to_cpu(super->fs_tfree); |
245 | sb->inode_free = be32_to_cpu(super->fs_tinode); |
246 | sb->inode_blocks = be16_to_cpu(super->fs_cgisize); |
247 | sb->total_groups = be16_to_cpu(super->fs_ncg); |
248 | |
249 | return 0; |
250 | } |
251 | |
252 | static int efs_fill_super(struct super_block *s, void *d, int silent) |
253 | { |
254 | struct efs_sb_info *sb; |
255 | struct buffer_head *bh; |
256 | struct inode *root; |
257 | |
258 | sb = kzalloc(size: sizeof(struct efs_sb_info), GFP_KERNEL); |
259 | if (!sb) |
260 | return -ENOMEM; |
261 | s->s_fs_info = sb; |
262 | s->s_time_min = 0; |
263 | s->s_time_max = U32_MAX; |
264 | |
265 | s->s_magic = EFS_SUPER_MAGIC; |
266 | if (!sb_set_blocksize(s, EFS_BLOCKSIZE)) { |
267 | pr_err("device does not support %d byte blocks\n" , |
268 | EFS_BLOCKSIZE); |
269 | return -EINVAL; |
270 | } |
271 | |
272 | /* read the vh (volume header) block */ |
273 | bh = sb_bread(sb: s, block: 0); |
274 | |
275 | if (!bh) { |
276 | pr_err("cannot read volume header\n" ); |
277 | return -EIO; |
278 | } |
279 | |
280 | /* |
281 | * if this returns zero then we didn't find any partition table. |
282 | * this isn't (yet) an error - just assume for the moment that |
283 | * the device is valid and go on to search for a superblock. |
284 | */ |
285 | sb->fs_start = efs_validate_vh(vh: (struct volume_header *) bh->b_data); |
286 | brelse(bh); |
287 | |
288 | if (sb->fs_start == -1) { |
289 | return -EINVAL; |
290 | } |
291 | |
292 | bh = sb_bread(sb: s, block: sb->fs_start + EFS_SUPER); |
293 | if (!bh) { |
294 | pr_err("cannot read superblock\n" ); |
295 | return -EIO; |
296 | } |
297 | |
298 | if (efs_validate_super(sb, super: (struct efs_super *) bh->b_data)) { |
299 | #ifdef DEBUG |
300 | pr_warn("invalid superblock at block %u\n" , |
301 | sb->fs_start + EFS_SUPER); |
302 | #endif |
303 | brelse(bh); |
304 | return -EINVAL; |
305 | } |
306 | brelse(bh); |
307 | |
308 | if (!sb_rdonly(sb: s)) { |
309 | #ifdef DEBUG |
310 | pr_info("forcing read-only mode\n" ); |
311 | #endif |
312 | s->s_flags |= SB_RDONLY; |
313 | } |
314 | s->s_op = &efs_superblock_operations; |
315 | s->s_export_op = &efs_export_ops; |
316 | root = efs_iget(s, EFS_ROOTINODE); |
317 | if (IS_ERR(ptr: root)) { |
318 | pr_err("get root inode failed\n" ); |
319 | return PTR_ERR(ptr: root); |
320 | } |
321 | |
322 | s->s_root = d_make_root(root); |
323 | if (!(s->s_root)) { |
324 | pr_err("get root dentry failed\n" ); |
325 | return -ENOMEM; |
326 | } |
327 | |
328 | return 0; |
329 | } |
330 | |
331 | static int efs_statfs(struct dentry *dentry, struct kstatfs *buf) { |
332 | struct super_block *sb = dentry->d_sb; |
333 | struct efs_sb_info *sbi = SUPER_INFO(sb); |
334 | u64 id = huge_encode_dev(dev: sb->s_bdev->bd_dev); |
335 | |
336 | buf->f_type = EFS_SUPER_MAGIC; /* efs magic number */ |
337 | buf->f_bsize = EFS_BLOCKSIZE; /* blocksize */ |
338 | buf->f_blocks = sbi->total_groups * /* total data blocks */ |
339 | (sbi->group_size - sbi->inode_blocks); |
340 | buf->f_bfree = sbi->data_free; /* free data blocks */ |
341 | buf->f_bavail = sbi->data_free; /* free blocks for non-root */ |
342 | buf->f_files = sbi->total_groups * /* total inodes */ |
343 | sbi->inode_blocks * |
344 | (EFS_BLOCKSIZE / sizeof(struct efs_dinode)); |
345 | buf->f_ffree = sbi->inode_free; /* free inodes */ |
346 | buf->f_fsid = u64_to_fsid(v: id); |
347 | buf->f_namelen = EFS_MAXNAMELEN; /* max filename length */ |
348 | |
349 | return 0; |
350 | } |
351 | |
352 | |