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/partitions/check.c |
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/partitions/check.c')
-rw-r--r-- | fs/partitions/check.c | 445 |
1 files changed, 445 insertions, 0 deletions
diff --git a/fs/partitions/check.c b/fs/partitions/check.c new file mode 100644 index 000000000000..31cff785b3bd --- /dev/null +++ b/fs/partitions/check.c | |||
@@ -0,0 +1,445 @@ | |||
1 | /* | ||
2 | * fs/partitions/check.c | ||
3 | * | ||
4 | * Code extracted from drivers/block/genhd.c | ||
5 | * Copyright (C) 1991-1998 Linus Torvalds | ||
6 | * Re-organised Feb 1998 Russell King | ||
7 | * | ||
8 | * We now have independent partition support from the | ||
9 | * block drivers, which allows all the partition code to | ||
10 | * be grouped in one location, and it to be mostly self | ||
11 | * contained. | ||
12 | * | ||
13 | * Added needed MAJORS for new pairs, {hdi,hdj}, {hdk,hdl} | ||
14 | */ | ||
15 | |||
16 | #include <linux/init.h> | ||
17 | #include <linux/module.h> | ||
18 | #include <linux/fs.h> | ||
19 | #include <linux/kmod.h> | ||
20 | #include <linux/ctype.h> | ||
21 | #include <linux/devfs_fs_kernel.h> | ||
22 | |||
23 | #include "check.h" | ||
24 | #include "devfs.h" | ||
25 | |||
26 | #include "acorn.h" | ||
27 | #include "amiga.h" | ||
28 | #include "atari.h" | ||
29 | #include "ldm.h" | ||
30 | #include "mac.h" | ||
31 | #include "msdos.h" | ||
32 | #include "osf.h" | ||
33 | #include "sgi.h" | ||
34 | #include "sun.h" | ||
35 | #include "ibm.h" | ||
36 | #include "ultrix.h" | ||
37 | #include "efi.h" | ||
38 | |||
39 | #ifdef CONFIG_BLK_DEV_MD | ||
40 | extern void md_autodetect_dev(dev_t dev); | ||
41 | #endif | ||
42 | |||
43 | int warn_no_part = 1; /*This is ugly: should make genhd removable media aware*/ | ||
44 | |||
45 | static int (*check_part[])(struct parsed_partitions *, struct block_device *) = { | ||
46 | /* | ||
47 | * Probe partition formats with tables at disk address 0 | ||
48 | * that also have an ADFS boot block at 0xdc0. | ||
49 | */ | ||
50 | #ifdef CONFIG_ACORN_PARTITION_ICS | ||
51 | adfspart_check_ICS, | ||
52 | #endif | ||
53 | #ifdef CONFIG_ACORN_PARTITION_POWERTEC | ||
54 | adfspart_check_POWERTEC, | ||
55 | #endif | ||
56 | #ifdef CONFIG_ACORN_PARTITION_EESOX | ||
57 | adfspart_check_EESOX, | ||
58 | #endif | ||
59 | |||
60 | /* | ||
61 | * Now move on to formats that only have partition info at | ||
62 | * disk address 0xdc0. Since these may also have stale | ||
63 | * PC/BIOS partition tables, they need to come before | ||
64 | * the msdos entry. | ||
65 | */ | ||
66 | #ifdef CONFIG_ACORN_PARTITION_CUMANA | ||
67 | adfspart_check_CUMANA, | ||
68 | #endif | ||
69 | #ifdef CONFIG_ACORN_PARTITION_ADFS | ||
70 | adfspart_check_ADFS, | ||
71 | #endif | ||
72 | |||
73 | #ifdef CONFIG_EFI_PARTITION | ||
74 | efi_partition, /* this must come before msdos */ | ||
75 | #endif | ||
76 | #ifdef CONFIG_SGI_PARTITION | ||
77 | sgi_partition, | ||
78 | #endif | ||
79 | #ifdef CONFIG_LDM_PARTITION | ||
80 | ldm_partition, /* this must come before msdos */ | ||
81 | #endif | ||
82 | #ifdef CONFIG_NEC98_PARTITION | ||
83 | nec98_partition, /* must be come before `msdos_partition' */ | ||
84 | #endif | ||
85 | #ifdef CONFIG_MSDOS_PARTITION | ||
86 | msdos_partition, | ||
87 | #endif | ||
88 | #ifdef CONFIG_OSF_PARTITION | ||
89 | osf_partition, | ||
90 | #endif | ||
91 | #ifdef CONFIG_SUN_PARTITION | ||
92 | sun_partition, | ||
93 | #endif | ||
94 | #ifdef CONFIG_AMIGA_PARTITION | ||
95 | amiga_partition, | ||
96 | #endif | ||
97 | #ifdef CONFIG_ATARI_PARTITION | ||
98 | atari_partition, | ||
99 | #endif | ||
100 | #ifdef CONFIG_MAC_PARTITION | ||
101 | mac_partition, | ||
102 | #endif | ||
103 | #ifdef CONFIG_ULTRIX_PARTITION | ||
104 | ultrix_partition, | ||
105 | #endif | ||
106 | #ifdef CONFIG_IBM_PARTITION | ||
107 | ibm_partition, | ||
108 | #endif | ||
109 | NULL | ||
110 | }; | ||
111 | |||
112 | /* | ||
113 | * disk_name() is used by partition check code and the genhd driver. | ||
114 | * It formats the devicename of the indicated disk into | ||
115 | * the supplied buffer (of size at least 32), and returns | ||
116 | * a pointer to that same buffer (for convenience). | ||
117 | */ | ||
118 | |||
119 | char *disk_name(struct gendisk *hd, int part, char *buf) | ||
120 | { | ||
121 | if (!part) | ||
122 | snprintf(buf, BDEVNAME_SIZE, "%s", hd->disk_name); | ||
123 | else if (isdigit(hd->disk_name[strlen(hd->disk_name)-1])) | ||
124 | snprintf(buf, BDEVNAME_SIZE, "%sp%d", hd->disk_name, part); | ||
125 | else | ||
126 | snprintf(buf, BDEVNAME_SIZE, "%s%d", hd->disk_name, part); | ||
127 | |||
128 | return buf; | ||
129 | } | ||
130 | |||
131 | const char *bdevname(struct block_device *bdev, char *buf) | ||
132 | { | ||
133 | int part = MINOR(bdev->bd_dev) - bdev->bd_disk->first_minor; | ||
134 | return disk_name(bdev->bd_disk, part, buf); | ||
135 | } | ||
136 | |||
137 | EXPORT_SYMBOL(bdevname); | ||
138 | |||
139 | /* | ||
140 | * There's very little reason to use this, you should really | ||
141 | * have a struct block_device just about everywhere and use | ||
142 | * bdevname() instead. | ||
143 | */ | ||
144 | const char *__bdevname(dev_t dev, char *buffer) | ||
145 | { | ||
146 | scnprintf(buffer, BDEVNAME_SIZE, "unknown-block(%u,%u)", | ||
147 | MAJOR(dev), MINOR(dev)); | ||
148 | return buffer; | ||
149 | } | ||
150 | |||
151 | EXPORT_SYMBOL(__bdevname); | ||
152 | |||
153 | static struct parsed_partitions * | ||
154 | check_partition(struct gendisk *hd, struct block_device *bdev) | ||
155 | { | ||
156 | struct parsed_partitions *state; | ||
157 | int i, res; | ||
158 | |||
159 | state = kmalloc(sizeof(struct parsed_partitions), GFP_KERNEL); | ||
160 | if (!state) | ||
161 | return NULL; | ||
162 | |||
163 | #ifdef CONFIG_DEVFS_FS | ||
164 | if (hd->devfs_name[0] != '\0') { | ||
165 | printk(KERN_INFO " /dev/%s:", hd->devfs_name); | ||
166 | sprintf(state->name, "p"); | ||
167 | } | ||
168 | #endif | ||
169 | else { | ||
170 | disk_name(hd, 0, state->name); | ||
171 | printk(KERN_INFO " %s:", state->name); | ||
172 | if (isdigit(state->name[strlen(state->name)-1])) | ||
173 | sprintf(state->name, "p"); | ||
174 | } | ||
175 | state->limit = hd->minors; | ||
176 | i = res = 0; | ||
177 | while (!res && check_part[i]) { | ||
178 | memset(&state->parts, 0, sizeof(state->parts)); | ||
179 | res = check_part[i++](state, bdev); | ||
180 | } | ||
181 | if (res > 0) | ||
182 | return state; | ||
183 | if (!res) | ||
184 | printk(" unknown partition table\n"); | ||
185 | else if (warn_no_part) | ||
186 | printk(" unable to read partition table\n"); | ||
187 | kfree(state); | ||
188 | return NULL; | ||
189 | } | ||
190 | |||
191 | /* | ||
192 | * sysfs bindings for partitions | ||
193 | */ | ||
194 | |||
195 | struct part_attribute { | ||
196 | struct attribute attr; | ||
197 | ssize_t (*show)(struct hd_struct *,char *); | ||
198 | }; | ||
199 | |||
200 | static ssize_t | ||
201 | part_attr_show(struct kobject * kobj, struct attribute * attr, char * page) | ||
202 | { | ||
203 | struct hd_struct * p = container_of(kobj,struct hd_struct,kobj); | ||
204 | struct part_attribute * part_attr = container_of(attr,struct part_attribute,attr); | ||
205 | ssize_t ret = 0; | ||
206 | if (part_attr->show) | ||
207 | ret = part_attr->show(p,page); | ||
208 | return ret; | ||
209 | } | ||
210 | |||
211 | static struct sysfs_ops part_sysfs_ops = { | ||
212 | .show = part_attr_show, | ||
213 | }; | ||
214 | |||
215 | static ssize_t part_dev_read(struct hd_struct * p, char *page) | ||
216 | { | ||
217 | struct gendisk *disk = container_of(p->kobj.parent,struct gendisk,kobj); | ||
218 | dev_t dev = MKDEV(disk->major, disk->first_minor + p->partno); | ||
219 | return print_dev_t(page, dev); | ||
220 | } | ||
221 | static ssize_t part_start_read(struct hd_struct * p, char *page) | ||
222 | { | ||
223 | return sprintf(page, "%llu\n",(unsigned long long)p->start_sect); | ||
224 | } | ||
225 | static ssize_t part_size_read(struct hd_struct * p, char *page) | ||
226 | { | ||
227 | return sprintf(page, "%llu\n",(unsigned long long)p->nr_sects); | ||
228 | } | ||
229 | static ssize_t part_stat_read(struct hd_struct * p, char *page) | ||
230 | { | ||
231 | return sprintf(page, "%8u %8llu %8u %8llu\n", | ||
232 | p->reads, (unsigned long long)p->read_sectors, | ||
233 | p->writes, (unsigned long long)p->write_sectors); | ||
234 | } | ||
235 | static struct part_attribute part_attr_dev = { | ||
236 | .attr = {.name = "dev", .mode = S_IRUGO }, | ||
237 | .show = part_dev_read | ||
238 | }; | ||
239 | static struct part_attribute part_attr_start = { | ||
240 | .attr = {.name = "start", .mode = S_IRUGO }, | ||
241 | .show = part_start_read | ||
242 | }; | ||
243 | static struct part_attribute part_attr_size = { | ||
244 | .attr = {.name = "size", .mode = S_IRUGO }, | ||
245 | .show = part_size_read | ||
246 | }; | ||
247 | static struct part_attribute part_attr_stat = { | ||
248 | .attr = {.name = "stat", .mode = S_IRUGO }, | ||
249 | .show = part_stat_read | ||
250 | }; | ||
251 | |||
252 | static struct attribute * default_attrs[] = { | ||
253 | &part_attr_dev.attr, | ||
254 | &part_attr_start.attr, | ||
255 | &part_attr_size.attr, | ||
256 | &part_attr_stat.attr, | ||
257 | NULL, | ||
258 | }; | ||
259 | |||
260 | extern struct subsystem block_subsys; | ||
261 | |||
262 | static void part_release(struct kobject *kobj) | ||
263 | { | ||
264 | struct hd_struct * p = container_of(kobj,struct hd_struct,kobj); | ||
265 | kfree(p); | ||
266 | } | ||
267 | |||
268 | struct kobj_type ktype_part = { | ||
269 | .release = part_release, | ||
270 | .default_attrs = default_attrs, | ||
271 | .sysfs_ops = &part_sysfs_ops, | ||
272 | }; | ||
273 | |||
274 | void delete_partition(struct gendisk *disk, int part) | ||
275 | { | ||
276 | struct hd_struct *p = disk->part[part-1]; | ||
277 | if (!p) | ||
278 | return; | ||
279 | if (!p->nr_sects) | ||
280 | return; | ||
281 | disk->part[part-1] = NULL; | ||
282 | p->start_sect = 0; | ||
283 | p->nr_sects = 0; | ||
284 | p->reads = p->writes = p->read_sectors = p->write_sectors = 0; | ||
285 | devfs_remove("%s/part%d", disk->devfs_name, part); | ||
286 | kobject_unregister(&p->kobj); | ||
287 | } | ||
288 | |||
289 | void add_partition(struct gendisk *disk, int part, sector_t start, sector_t len) | ||
290 | { | ||
291 | struct hd_struct *p; | ||
292 | |||
293 | p = kmalloc(sizeof(*p), GFP_KERNEL); | ||
294 | if (!p) | ||
295 | return; | ||
296 | |||
297 | memset(p, 0, sizeof(*p)); | ||
298 | p->start_sect = start; | ||
299 | p->nr_sects = len; | ||
300 | p->partno = part; | ||
301 | |||
302 | devfs_mk_bdev(MKDEV(disk->major, disk->first_minor + part), | ||
303 | S_IFBLK|S_IRUSR|S_IWUSR, | ||
304 | "%s/part%d", disk->devfs_name, part); | ||
305 | |||
306 | if (isdigit(disk->kobj.name[strlen(disk->kobj.name)-1])) | ||
307 | snprintf(p->kobj.name,KOBJ_NAME_LEN,"%sp%d",disk->kobj.name,part); | ||
308 | else | ||
309 | snprintf(p->kobj.name,KOBJ_NAME_LEN,"%s%d",disk->kobj.name,part); | ||
310 | p->kobj.parent = &disk->kobj; | ||
311 | p->kobj.ktype = &ktype_part; | ||
312 | kobject_register(&p->kobj); | ||
313 | disk->part[part-1] = p; | ||
314 | } | ||
315 | |||
316 | static void disk_sysfs_symlinks(struct gendisk *disk) | ||
317 | { | ||
318 | struct device *target = get_device(disk->driverfs_dev); | ||
319 | if (target) { | ||
320 | sysfs_create_link(&disk->kobj,&target->kobj,"device"); | ||
321 | sysfs_create_link(&target->kobj,&disk->kobj,"block"); | ||
322 | } | ||
323 | } | ||
324 | |||
325 | /* Not exported, helper to add_disk(). */ | ||
326 | void register_disk(struct gendisk *disk) | ||
327 | { | ||
328 | struct block_device *bdev; | ||
329 | char *s; | ||
330 | int err; | ||
331 | |||
332 | strlcpy(disk->kobj.name,disk->disk_name,KOBJ_NAME_LEN); | ||
333 | /* ewww... some of these buggers have / in name... */ | ||
334 | s = strchr(disk->kobj.name, '/'); | ||
335 | if (s) | ||
336 | *s = '!'; | ||
337 | if ((err = kobject_add(&disk->kobj))) | ||
338 | return; | ||
339 | disk_sysfs_symlinks(disk); | ||
340 | |||
341 | /* No minors to use for partitions */ | ||
342 | if (disk->minors == 1) { | ||
343 | if (disk->devfs_name[0] != '\0') | ||
344 | devfs_add_disk(disk); | ||
345 | return; | ||
346 | } | ||
347 | |||
348 | /* always add handle for the whole disk */ | ||
349 | devfs_add_partitioned(disk); | ||
350 | |||
351 | /* No such device (e.g., media were just removed) */ | ||
352 | if (!get_capacity(disk)) | ||
353 | return; | ||
354 | |||
355 | bdev = bdget_disk(disk, 0); | ||
356 | if (!bdev) | ||
357 | return; | ||
358 | |||
359 | bdev->bd_invalidated = 1; | ||
360 | if (blkdev_get(bdev, FMODE_READ, 0) < 0) | ||
361 | return; | ||
362 | blkdev_put(bdev); | ||
363 | } | ||
364 | |||
365 | int rescan_partitions(struct gendisk *disk, struct block_device *bdev) | ||
366 | { | ||
367 | struct parsed_partitions *state; | ||
368 | int p, res; | ||
369 | |||
370 | if (bdev->bd_part_count) | ||
371 | return -EBUSY; | ||
372 | res = invalidate_partition(disk, 0); | ||
373 | if (res) | ||
374 | return res; | ||
375 | bdev->bd_invalidated = 0; | ||
376 | for (p = 1; p < disk->minors; p++) | ||
377 | delete_partition(disk, p); | ||
378 | if (disk->fops->revalidate_disk) | ||
379 | disk->fops->revalidate_disk(disk); | ||
380 | if (!get_capacity(disk) || !(state = check_partition(disk, bdev))) | ||
381 | return 0; | ||
382 | for (p = 1; p < state->limit; p++) { | ||
383 | sector_t size = state->parts[p].size; | ||
384 | sector_t from = state->parts[p].from; | ||
385 | if (!size) | ||
386 | continue; | ||
387 | add_partition(disk, p, from, size); | ||
388 | #ifdef CONFIG_BLK_DEV_MD | ||
389 | if (state->parts[p].flags) | ||
390 | md_autodetect_dev(bdev->bd_dev+p); | ||
391 | #endif | ||
392 | } | ||
393 | kfree(state); | ||
394 | return 0; | ||
395 | } | ||
396 | |||
397 | unsigned char *read_dev_sector(struct block_device *bdev, sector_t n, Sector *p) | ||
398 | { | ||
399 | struct address_space *mapping = bdev->bd_inode->i_mapping; | ||
400 | struct page *page; | ||
401 | |||
402 | page = read_cache_page(mapping, (pgoff_t)(n >> (PAGE_CACHE_SHIFT-9)), | ||
403 | (filler_t *)mapping->a_ops->readpage, NULL); | ||
404 | if (!IS_ERR(page)) { | ||
405 | wait_on_page_locked(page); | ||
406 | if (!PageUptodate(page)) | ||
407 | goto fail; | ||
408 | if (PageError(page)) | ||
409 | goto fail; | ||
410 | p->v = page; | ||
411 | return (unsigned char *)page_address(page) + ((n & ((1 << (PAGE_CACHE_SHIFT - 9)) - 1)) << 9); | ||
412 | fail: | ||
413 | page_cache_release(page); | ||
414 | } | ||
415 | p->v = NULL; | ||
416 | return NULL; | ||
417 | } | ||
418 | |||
419 | EXPORT_SYMBOL(read_dev_sector); | ||
420 | |||
421 | void del_gendisk(struct gendisk *disk) | ||
422 | { | ||
423 | int p; | ||
424 | |||
425 | /* invalidate stuff */ | ||
426 | for (p = disk->minors - 1; p > 0; p--) { | ||
427 | invalidate_partition(disk, p); | ||
428 | delete_partition(disk, p); | ||
429 | } | ||
430 | invalidate_partition(disk, 0); | ||
431 | disk->capacity = 0; | ||
432 | disk->flags &= ~GENHD_FL_UP; | ||
433 | unlink_gendisk(disk); | ||
434 | disk_stat_set_all(disk, 0); | ||
435 | disk->stamp = disk->stamp_idle = 0; | ||
436 | |||
437 | devfs_remove_disk(disk); | ||
438 | |||
439 | if (disk->driverfs_dev) { | ||
440 | sysfs_remove_link(&disk->kobj, "device"); | ||
441 | sysfs_remove_link(&disk->driverfs_dev->kobj, "block"); | ||
442 | put_device(disk->driverfs_dev); | ||
443 | } | ||
444 | kobject_del(&disk->kobj); | ||
445 | } | ||