| /* | |
| * JFFS -- Journaling Flash File System, Linux implementation. | |
| * | |
| * Copyright (C) 2000 Axis Communications AB. | |
| * | |
| * Created by Simon Kagstrom <simonk@axis.com>. | |
| * | |
| * $Id: jffs_proc.c,v 1.5 2001/06/02 14:34:55 dwmw2 Exp $ | |
| * | |
| * This is free software; you can redistribute it and/or modify it | |
| * under the terms of the GNU General Public License as published by | |
| * the Free Software Foundation; either version 2 of the License, or | |
| * (at your option) any later version. | |
| * | |
| * Overview: | |
| * This file defines JFFS partition entries in the proc file system. | |
| * | |
| * TODO: | |
| * Create some more proc files for different kinds of info, i.e. statistics | |
| * about written and read bytes, number of calls to different routines, | |
| * reports about failures. | |
| */ | |
| #include <linux/errno.h> | |
| #include <linux/fs.h> | |
| #include <linux/jffs.h> | |
| #include <linux/slab.h> | |
| #include <linux/proc_fs.h> | |
| #include <linux/time.h> | |
| #include <linux/types.h> | |
| #include "jffs_fm.h" | |
| #include "jffs_proc.h" | |
| /* | |
| * Structure for a JFFS partition in the system | |
| */ | |
| struct jffs_partition_dir { | |
| struct jffs_control *c; | |
| struct proc_dir_entry *part_root; | |
| struct proc_dir_entry *part_info; | |
| struct proc_dir_entry *part_layout; | |
| struct jffs_partition_dir *next; | |
| }; | |
| /* | |
| * Structure for top-level entry in '/proc/fs' directory | |
| */ | |
| struct proc_dir_entry *jffs_proc_root; | |
| /* | |
| * Linked list of 'jffs_partition_dirs' to help us track | |
| * the mounted JFFS partitions in the system | |
| */ | |
| static struct jffs_partition_dir *jffs_part_dirs; | |
| /* | |
| * Read functions for entries | |
| */ | |
| static int jffs_proc_info_read(char *page, char **start, off_t off, | |
| int count, int *eof, void *data); | |
| static int jffs_proc_layout_read (char *page, char **start, off_t off, | |
| int count, int *eof, void *data); | |
| /* | |
| * Register a JFFS partition directory (called upon mount) | |
| */ | |
| int jffs_register_jffs_proc_dir(int mtd, struct jffs_control *c) | |
| { | |
| struct jffs_partition_dir *part_dir; | |
| struct proc_dir_entry *part_info = NULL; | |
| struct proc_dir_entry *part_layout = NULL; | |
| struct proc_dir_entry *part_root = NULL; | |
| char name[10]; | |
| sprintf(name, "%d", mtd); | |
| /* Allocate structure for local JFFS partition table */ | |
| part_dir = (struct jffs_partition_dir *) | |
| kmalloc(sizeof (struct jffs_partition_dir), GFP_KERNEL); | |
| if (!part_dir) | |
| goto out; | |
| /* Create entry for this partition */ | |
| part_root = proc_mkdir(name, jffs_proc_root); | |
| if (!part_root) | |
| goto out1; | |
| /* Create entry for 'info' file */ | |
| part_info = create_proc_entry ("info", 0, part_root); | |
| if (!part_info) | |
| goto out2; | |
| part_info->read_proc = jffs_proc_info_read; | |
| part_info->data = (void *) c; | |
| /* Create entry for 'layout' file */ | |
| part_layout = create_proc_entry ("layout", 0, part_root); | |
| if (!part_layout) | |
| goto out3; | |
| part_layout->read_proc = jffs_proc_layout_read; | |
| part_layout->data = (void *) c; | |
| /* Fill in structure for table and insert in the list */ | |
| part_dir->c = c; | |
| part_dir->part_root = part_root; | |
| part_dir->part_info = part_info; | |
| part_dir->part_layout = part_layout; | |
| part_dir->next = jffs_part_dirs; | |
| jffs_part_dirs = part_dir; | |
| /* Return happy */ | |
| return 0; | |
| out3: | |
| remove_proc_entry("info", part_root); | |
| out2: | |
| remove_proc_entry(name, jffs_proc_root); | |
| out1: | |
| kfree(part_dir); | |
| out: | |
| return -ENOMEM; | |
| } | |
| /* | |
| * Unregister a JFFS partition directory (called at umount) | |
| */ | |
| int jffs_unregister_jffs_proc_dir(struct jffs_control *c) | |
| { | |
| struct jffs_partition_dir *part_dir = jffs_part_dirs; | |
| struct jffs_partition_dir *prev_part_dir = NULL; | |
| while (part_dir) { | |
| if (part_dir->c == c) { | |
| /* Remove entries for partition */ | |
| remove_proc_entry (part_dir->part_info->name, | |
| part_dir->part_root); | |
| remove_proc_entry (part_dir->part_layout->name, | |
| part_dir->part_root); | |
| remove_proc_entry (part_dir->part_root->name, | |
| jffs_proc_root); | |
| /* Remove entry from list */ | |
| if (prev_part_dir) | |
| prev_part_dir->next = part_dir->next; | |
| else | |
| jffs_part_dirs = part_dir->next; | |
| /* | |
| * Check to see if this is the last one | |
| * and remove the entry from '/proc/fs' | |
| * if it is. | |
| */ | |
| if (jffs_part_dirs == part_dir->next) | |
| remove_proc_entry ("jffs", proc_root_fs); | |
| /* Free memory for entry */ | |
| kfree(part_dir); | |
| /* Return happy */ | |
| return 0; | |
| } | |
| /* Move to next entry */ | |
| prev_part_dir = part_dir; | |
| part_dir = part_dir->next; | |
| } | |
| /* Return unhappy */ | |
| return -1; | |
| } | |
| /* | |
| * Read a JFFS partition's `info' file | |
| */ | |
| static int jffs_proc_info_read (char *page, char **start, off_t off, | |
| int count, int *eof, void *data) | |
| { | |
| struct jffs_control *c = (struct jffs_control *) data; | |
| int len = 0; | |
| /* Get information on the parition */ | |
| len += sprintf (page, | |
| "partition size: %08lX (%u)\n" | |
| "sector size: %08lX (%u)\n" | |
| "used size: %08lX (%u)\n" | |
| "dirty size: %08lX (%u)\n" | |
| "free size: %08lX (%u)\n\n", | |
| (unsigned long) c->fmc->flash_size, c->fmc->flash_size, | |
| (unsigned long) c->fmc->sector_size, c->fmc->sector_size, | |
| (unsigned long) c->fmc->used_size, c->fmc->used_size, | |
| (unsigned long) c->fmc->dirty_size, c->fmc->dirty_size, | |
| (unsigned long) (c->fmc->flash_size - | |
| (c->fmc->used_size + c->fmc->dirty_size)), | |
| c->fmc->flash_size - (c->fmc->used_size + c->fmc->dirty_size)); | |
| /* We're done */ | |
| *eof = 1; | |
| /* Return length */ | |
| return len; | |
| } | |
| /* | |
| * Read a JFFS partition's `layout' file | |
| */ | |
| static int jffs_proc_layout_read (char *page, char **start, off_t off, | |
| int count, int *eof, void *data) | |
| { | |
| struct jffs_control *c = (struct jffs_control *) data; | |
| struct jffs_fm *fm = NULL; | |
| struct jffs_fm *last_fm = NULL; | |
| int len = 0; | |
| /* Get the first item in the list */ | |
| fm = c->fmc->head; | |
| /* Print free space */ | |
| if (fm && fm->offset) { | |
| len += sprintf (page, "00000000 %08lX free\n", | |
| (unsigned long) fm->offset); | |
| } | |
| /* Loop through all of the flash control structures */ | |
| while (fm && (len < (off + count))) { | |
| if (fm->nodes) { | |
| len += sprintf (page + len, | |
| "%08lX %08lX ino=%08lX, ver=%08lX\n", | |
| (unsigned long) fm->offset, | |
| (unsigned long) fm->size, | |
| (unsigned long) fm->nodes->node->ino, | |
| (unsigned long) fm->nodes->node->version); | |
| } | |
| else { | |
| len += sprintf (page + len, | |
| "%08lX %08lX dirty\n", | |
| (unsigned long) fm->offset, | |
| (unsigned long) fm->size); | |
| } | |
| last_fm = fm; | |
| fm = fm->next; | |
| } | |
| /* Print free space */ | |
| if ((len < (off + count)) && last_fm | |
| && (last_fm->offset < c->fmc->flash_size)) { | |
| len += sprintf (page + len, | |
| "%08lX %08lX free\n", | |
| (unsigned long) last_fm->offset + | |
| last_fm->size, | |
| (unsigned long) (c->fmc->flash_size - | |
| (last_fm->offset + last_fm->size))); | |
| } | |
| /* We're done */ | |
| *eof = 1; | |
| /* Return length */ | |
| return len; | |
| } |