blob: 9bdd99a557c27dde4659f46ec0f180f2a58ddf52 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * JFFS -- Journaling Flash File System, Linux implementation.
3 *
4 * Copyright (C) 2000 Axis Communications AB.
5 *
6 * Created by Simon Kagstrom <simonk@axis.com>.
7 *
8 * $Id: jffs_proc.c,v 1.5 2001/06/02 14:34:55 dwmw2 Exp $
9 *
10 * This is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * Overview:
16 * This file defines JFFS partition entries in the proc file system.
17 *
18 * TODO:
19 * Create some more proc files for different kinds of info, i.e. statistics
20 * about written and read bytes, number of calls to different routines,
21 * reports about failures.
22 */
23
24#include <linux/errno.h>
25#include <linux/fs.h>
26#include <linux/jffs.h>
27#include <linux/slab.h>
28#include <linux/proc_fs.h>
29#include <linux/time.h>
30#include <linux/types.h>
31#include "jffs_fm.h"
32#include "jffs_proc.h"
33
34/*
35 * Structure for a JFFS partition in the system
36 */
37struct jffs_partition_dir {
38 struct jffs_control *c;
39 struct proc_dir_entry *part_root;
40 struct proc_dir_entry *part_info;
41 struct proc_dir_entry *part_layout;
42 struct jffs_partition_dir *next;
43};
44
45/*
46 * Structure for top-level entry in '/proc/fs' directory
47 */
48struct proc_dir_entry *jffs_proc_root;
49
50/*
51 * Linked list of 'jffs_partition_dirs' to help us track
52 * the mounted JFFS partitions in the system
53 */
54static struct jffs_partition_dir *jffs_part_dirs;
55
56/*
57 * Read functions for entries
58 */
59static int jffs_proc_info_read(char *page, char **start, off_t off,
60 int count, int *eof, void *data);
61static int jffs_proc_layout_read (char *page, char **start, off_t off,
62 int count, int *eof, void *data);
63
64
65/*
66 * Register a JFFS partition directory (called upon mount)
67 */
68int jffs_register_jffs_proc_dir(int mtd, struct jffs_control *c)
69{
70 struct jffs_partition_dir *part_dir;
71 struct proc_dir_entry *part_info = NULL;
72 struct proc_dir_entry *part_layout = NULL;
73 struct proc_dir_entry *part_root = NULL;
74 char name[10];
75
76 sprintf(name, "%d", mtd);
77 /* Allocate structure for local JFFS partition table */
78 part_dir = (struct jffs_partition_dir *)
79 kmalloc(sizeof (struct jffs_partition_dir), GFP_KERNEL);
80 if (!part_dir)
81 goto out;
82
83 /* Create entry for this partition */
84 part_root = proc_mkdir(name, jffs_proc_root);
85 if (!part_root)
86 goto out1;
87
88 /* Create entry for 'info' file */
89 part_info = create_proc_entry ("info", 0, part_root);
90 if (!part_info)
91 goto out2;
92 part_info->read_proc = jffs_proc_info_read;
93 part_info->data = (void *) c;
94
95 /* Create entry for 'layout' file */
96 part_layout = create_proc_entry ("layout", 0, part_root);
97 if (!part_layout)
98 goto out3;
99 part_layout->read_proc = jffs_proc_layout_read;
100 part_layout->data = (void *) c;
101
102 /* Fill in structure for table and insert in the list */
103 part_dir->c = c;
104 part_dir->part_root = part_root;
105 part_dir->part_info = part_info;
106 part_dir->part_layout = part_layout;
107 part_dir->next = jffs_part_dirs;
108 jffs_part_dirs = part_dir;
109
110 /* Return happy */
111 return 0;
112
113out3:
114 remove_proc_entry("info", part_root);
115out2:
116 remove_proc_entry(name, jffs_proc_root);
117out1:
118 kfree(part_dir);
119out:
120 return -ENOMEM;
121}
122
123
124/*
125 * Unregister a JFFS partition directory (called at umount)
126 */
127int jffs_unregister_jffs_proc_dir(struct jffs_control *c)
128{
129 struct jffs_partition_dir *part_dir = jffs_part_dirs;
130 struct jffs_partition_dir *prev_part_dir = NULL;
131
132 while (part_dir) {
133 if (part_dir->c == c) {
134 /* Remove entries for partition */
135 remove_proc_entry (part_dir->part_info->name,
136 part_dir->part_root);
137 remove_proc_entry (part_dir->part_layout->name,
138 part_dir->part_root);
139 remove_proc_entry (part_dir->part_root->name,
140 jffs_proc_root);
141
142 /* Remove entry from list */
143 if (prev_part_dir)
144 prev_part_dir->next = part_dir->next;
145 else
146 jffs_part_dirs = part_dir->next;
147
148 /*
149 * Check to see if this is the last one
150 * and remove the entry from '/proc/fs'
151 * if it is.
152 */
153 if (jffs_part_dirs == part_dir->next)
154 remove_proc_entry ("jffs", proc_root_fs);
155
156 /* Free memory for entry */
157 kfree(part_dir);
158
159 /* Return happy */
160 return 0;
161 }
162
163 /* Move to next entry */
164 prev_part_dir = part_dir;
165 part_dir = part_dir->next;
166 }
167
168 /* Return unhappy */
169 return -1;
170}
171
172
173/*
174 * Read a JFFS partition's `info' file
175 */
176static int jffs_proc_info_read (char *page, char **start, off_t off,
177 int count, int *eof, void *data)
178{
179 struct jffs_control *c = (struct jffs_control *) data;
180 int len = 0;
181
182 /* Get information on the parition */
183 len += sprintf (page,
184 "partition size: %08lX (%u)\n"
185 "sector size: %08lX (%u)\n"
186 "used size: %08lX (%u)\n"
187 "dirty size: %08lX (%u)\n"
188 "free size: %08lX (%u)\n\n",
189 (unsigned long) c->fmc->flash_size, c->fmc->flash_size,
190 (unsigned long) c->fmc->sector_size, c->fmc->sector_size,
191 (unsigned long) c->fmc->used_size, c->fmc->used_size,
192 (unsigned long) c->fmc->dirty_size, c->fmc->dirty_size,
193 (unsigned long) (c->fmc->flash_size -
194 (c->fmc->used_size + c->fmc->dirty_size)),
195 c->fmc->flash_size - (c->fmc->used_size + c->fmc->dirty_size));
196
197 /* We're done */
198 *eof = 1;
199
200 /* Return length */
201 return len;
202}
203
204
205/*
206 * Read a JFFS partition's `layout' file
207 */
208static int jffs_proc_layout_read (char *page, char **start, off_t off,
209 int count, int *eof, void *data)
210{
211 struct jffs_control *c = (struct jffs_control *) data;
212 struct jffs_fm *fm = NULL;
213 struct jffs_fm *last_fm = NULL;
214 int len = 0;
215
216 /* Get the first item in the list */
217 fm = c->fmc->head;
218
219 /* Print free space */
220 if (fm && fm->offset) {
221 len += sprintf (page, "00000000 %08lX free\n",
222 (unsigned long) fm->offset);
223 }
224
225 /* Loop through all of the flash control structures */
226 while (fm && (len < (off + count))) {
227 if (fm->nodes) {
228 len += sprintf (page + len,
229 "%08lX %08lX ino=%08lX, ver=%08lX\n",
230 (unsigned long) fm->offset,
231 (unsigned long) fm->size,
232 (unsigned long) fm->nodes->node->ino,
233 (unsigned long) fm->nodes->node->version);
234 }
235 else {
236 len += sprintf (page + len,
237 "%08lX %08lX dirty\n",
238 (unsigned long) fm->offset,
239 (unsigned long) fm->size);
240 }
241 last_fm = fm;
242 fm = fm->next;
243 }
244
245 /* Print free space */
246 if ((len < (off + count)) && last_fm
247 && (last_fm->offset < c->fmc->flash_size)) {
248 len += sprintf (page + len,
249 "%08lX %08lX free\n",
250 (unsigned long) last_fm->offset +
251 last_fm->size,
252 (unsigned long) (c->fmc->flash_size -
253 (last_fm->offset + last_fm->size)));
254 }
255
256 /* We're done */
257 *eof = 1;
258
259 /* Return length */
260 return len;
261}