1 /* This file deals with inode management.
3 * The entry points into this file are:
4 * init_inode initialize the inode table, return the root inode
5 * find_inode find an inode based on its inode number
6 * get_inode increase the reference count of an inode
7 * put_inode decrease the reference count of an inode
8 * link_inode link an inode as a directory entry to another inode
9 * unlink_inode unlink an inode from its parent directory
10 * get_free_inode return a free inode object
11 * have_free_inode check whether there is a free inode available
12 * have_used_inode check whether any inode is still in use
13 * do_putnode perform the PUTNODE file system call
16 * April 2009 (D.C. van Moolenbroek)
21 static struct inode inodes
[NUM_INODES
];
23 static TAILQ_HEAD(free_head
, inode
) free_list
;
25 /*===========================================================================*
27 *===========================================================================*/
28 struct inode
*init_inode()
30 /* Initialize inode table. Return the root inode.
35 TAILQ_INIT(&free_list
);
37 dprintf(("%s: %d inodes, %u bytes each, equals %u bytes\n",
38 sffs_name
, NUM_INODES
, sizeof(struct inode
), sizeof(inodes
)));
40 /* Mark all inodes except the root inode as free. */
41 for (index
= 1; index
< NUM_INODES
; index
++) {
44 LIST_INIT(&ino
->i_child
);
45 ino
->i_num
= index
+ 1;
46 ino
->i_gen
= (unsigned short)-1; /* aesthetics */
49 TAILQ_INSERT_TAIL(&free_list
, ino
, i_free
);
52 /* Initialize and return the root inode. */
54 ino
->i_parent
= ino
; /* root inode is its own parent */
55 LIST_INIT(&ino
->i_child
);
56 ino
->i_num
= ROOT_INODE_NR
;
57 ino
->i_gen
= 0; /* unused by root node */
58 ino
->i_ref
= 1; /* root inode is hereby in use */
59 ino
->i_flags
= I_DIR
; /* root inode is a directory */
60 ino
->i_name
[0] = 0; /* root inode has empty name */
65 /*===========================================================================*
67 *===========================================================================*/
68 struct inode
*find_inode(ino_nr
)
71 /* Get an inode based on its inode number. Do not increase its reference count.
76 /* Inode 0 (= index -1) is not a valid inode number. */
77 index
= INODE_INDEX(ino_nr
);
79 printf("%s: VFS passed invalid inode number!\n", sffs_name
);
84 assert(index
< NUM_INODES
);
88 /* Make sure the generation number matches. */
89 if (INODE_GEN(ino_nr
) != ino
->i_gen
) {
90 printf("%s: VFS passed outdated inode number!\n", sffs_name
);
95 /* The VFS/FS protocol only uses referenced inodes. */
97 printf("%s: VFS passed unused inode!\n", sffs_name
);
102 /*===========================================================================*
104 *===========================================================================*/
108 /* Increase the given inode's reference count. If both reference and link
109 * count were zero before, remove the inode from the free list.
112 dprintf(("%s: get_inode(%p) ['%s']\n", sffs_name
, ino
, ino
->i_name
));
114 /* (INUSE, CACHED) -> INUSE */
116 /* If this is the first reference, remove the node from the free list. */
117 if (ino
->i_ref
== 0 && !HAS_CHILDREN(ino
))
118 TAILQ_REMOVE(&free_list
, ino
, i_free
);
123 panic("inode reference count wrapped");
126 /*===========================================================================*
128 *===========================================================================*/
132 /* Decrease an inode's reference count. If this count has reached zero, close
133 * the inode's file handle, if any. If both reference and link count have
134 * reached zero, mark the inode as cached or free.
137 dprintf(("%s: put_inode(%p) ['%s']\n", sffs_name
, ino
, ino
->i_name
));
140 assert(ino
->i_ref
> 0);
144 /* If there are still references to this inode, we're done here. */
148 /* Close any file handle associated with this inode. */
151 /* Only add the inode to the free list if there are also no links to it. */
152 if (HAS_CHILDREN(ino
))
155 /* INUSE -> CACHED, DELETED -> FREE */
157 /* Add the inode to the head or tail of the free list, depending on whether
158 * it is also deleted (and therefore can never be reused as is).
160 if (ino
->i_parent
== NULL
)
161 TAILQ_INSERT_HEAD(&free_list
, ino
, i_free
);
163 TAILQ_INSERT_TAIL(&free_list
, ino
, i_free
);
166 /*===========================================================================*
168 *===========================================================================*/
169 void link_inode(parent
, ino
)
170 struct inode
*parent
;
173 /* Link an inode to a parent. If both reference and link count were zero
174 * before, remove the inode from the free list. This function should only be
175 * called from add_dentry().
178 /* This can never happen, right? */
179 if (parent
->i_ref
== 0 && !HAS_CHILDREN(parent
))
180 TAILQ_REMOVE(&free_list
, parent
, i_free
);
182 LIST_INSERT_HEAD(&parent
->i_child
, ino
, i_next
);
184 ino
->i_parent
= parent
;
187 /*===========================================================================*
189 *===========================================================================*/
190 void unlink_inode(ino
)
193 /* Unlink an inode from its parent. If both reference and link count have
194 * reached zero, mark the inode as cached or free. This function should only
195 * be used from del_dentry().
197 struct inode
*parent
;
199 parent
= ino
->i_parent
;
201 LIST_REMOVE(ino
, i_next
);
203 if (parent
->i_ref
== 0 && !HAS_CHILDREN(parent
)) {
204 if (parent
->i_parent
== NULL
)
205 TAILQ_INSERT_HEAD(&free_list
, parent
, i_free
);
207 TAILQ_INSERT_TAIL(&free_list
, parent
, i_free
);
210 ino
->i_parent
= NULL
;
213 /*===========================================================================*
215 *===========================================================================*/
216 struct inode
*get_free_inode()
218 /* Return a free inode object (with reference count 1), if available.
222 /* [CACHED -> FREE,] FREE -> DELETED */
224 /* If there are no inodes on the free list, we cannot satisfy the request. */
225 if (TAILQ_EMPTY(&free_list
)) {
226 printf("%s: out of inodes!\n", sffs_name
);
231 ino
= TAILQ_FIRST(&free_list
);
232 TAILQ_REMOVE(&free_list
, ino
, i_free
);
234 assert(ino
->i_ref
== 0);
235 assert(!HAS_CHILDREN(ino
));
237 /* If this was a cached inode, free it first. */
238 if (ino
->i_parent
!= NULL
)
241 assert(ino
->i_parent
== NULL
);
243 /* Initialize a subset of its fields */
250 /*===========================================================================*
252 *===========================================================================*/
253 int have_free_inode()
255 /* Check whether there are any free inodes at the moment. Kind of lame, but
256 * this allows for easier error recovery in some places.
259 return !TAILQ_EMPTY(&free_list
);
262 /*===========================================================================*
264 *===========================================================================*/
265 int have_used_inode()
267 /* Check whether any inodes are still in use, that is, any of the inodes have
268 * a reference count larger than zero.
272 for (index
= 0; index
< NUM_INODES
; index
++)
273 if (inodes
[index
].i_ref
> 0)
279 /*===========================================================================*
281 *===========================================================================*/
284 /* Decrease an inode's reference count.
289 if ((ino
= find_inode(m_in
.REQ_INODE_NR
)) == NULL
)
292 count
= m_in
.REQ_COUNT
;
294 if (count
<= 0 || count
> ino
->i_ref
) return EINVAL
;
296 ino
->i_ref
-= count
- 1;