kbd: use a better get_key method
[thunix.git] / fs / tfs / dir.c
blob9e56dd88ddd93ec73339aac8022ad47422394f2c
1 #include <stdio.h>
2 #include <string.h>
3 #include <malloc.h>
4 #include <err.h>
5 #include <fs.h>
6 #include <tfs.h>
7 #include <cache.h>
8 #include <dirent.h>
11 * NOTE! unlike strncmp, ext2_match_entry returns 1 for success, 0 for failure.
13 * len <= EXT2_NAME_LEN and de != NULL are guaranteed by caller.
15 static inline int tfs_match_entry (const char * const name,
16 struct tfs_dir_entry * de)
18 if (!de->d_inode)
19 return 0;
20 if (strlen(name) != strlen(de->d_name) && strlen(name) != TFS_NAME_LEN)
21 return 0;
22 return !strncmp(name, de->d_name, strlen(name));
26 * Returns:
27 * NULL, entry not found
28 * ERROR, errors happend.
29 * cs, entry found
31 struct cache_struct * tfs_find_entry(struct inode *inode, const char *dname, struct tfs_dir_entry **res)
33 uint32_t block;
34 int index = 0;
35 struct tfs_dir_entry *de;
36 struct cache_struct *cs;
37 struct tfs_sb_info *sbi = TFS_SBI(inode->i_fs);
39 block = inode->i_data[index++];
40 if (!block)
41 return NULL;
42 cs = get_cache_block(inode->i_fs, block);
43 if (!cs)
44 return ERR_PTR(-EIO);
45 de = (struct tfs_dir_entry *)cs->data;
47 while(1) {
48 if ((char *)de >= (char *)cs->data + sbi->s_block_size) {
49 if ((block = inode->i_data[index++]) < sbi->s_data_area)
50 return NULL;
51 cs = get_cache_block(inode->i_fs, block);
52 if (!cs)
53 return ERR_PTR(-EIO);
54 de = (struct tfs_dir_entry *)cs->data;
56 if (de->d_inode == 0) {
57 de++;
58 continue;
60 if (tfs_match_entry(dname, de)) {
61 *res = de;
62 return cs;
65 de++;
68 return NULL;
71 int tfs_add_entry(struct inode *dir, const char *name, int inr, int * dirty)
73 uint32_t block;
74 int index = 0;
75 struct cache_struct *cs;
76 struct tfs_dir_entry *de;
77 struct tfs_sb_info *sbi = TFS_SBI(dir->i_fs);
79 if (strlen(name) > TFS_NAME_LEN)
80 return -ENAMETOOLONG;
82 if (!(block = dir->i_data[index++]))
83 goto alloc_new_block;
84 cs = get_cache_block(dir->i_fs, block);
85 if (!cs)
86 return -EIO;
87 de = (struct tfs_dir_entry *)cs->data;
88 while (1) {
89 if ((void *)de >= cs->data + sbi->s_block_size) {
90 if (!(block = dir->i_data[index++]))
91 break;
92 cs = get_cache_block(dir->i_fs, block);
93 if (!cs)
94 return -EIO;
95 de = (struct tfs_dir_entry *)cs->data;
97 if (!de->d_inode)
98 break;
99 de++;
102 *dirty = 0;
104 alloc_new_block:
105 /* allocate a new block to hold the new entry */
106 if (!block) {
107 block = tfs_alloc_block(sbi, sbi->s_data_area);
108 if (block < 0)
109 return -ENOSPC;
110 if (index > TFS_N_BLOCKS)
111 return -EFBIG;
113 dir->i_data[index - 1] = block;
114 cs = get_cache_block(dir->i_fs, block);
115 if (!cs)
116 return -EIO;
117 de = (struct tfs_dir_entry *)cs->data;
118 memset(cs->data, 0, sbi->s_block_size);
121 /* Add a new entry at last */
122 dir->i_size += sizeof(struct tfs_dir_entry);
123 /* tell the caller to update this inode */
124 *dirty = 1;
126 memset(de, 0, sizeof(*de));
127 de->d_inode = inr;
128 memcpy(de->d_name, name, strlen(name));
130 /* write the entry back to disk */
131 if (tfs_bwrite(sbi, block, cs->data))
132 return -EIO;
134 return 0;
138 int tfs_mkdir(struct inode *parent_dir, const char *dname)
140 struct inode *dir;
141 int dirty;
142 int err = 0;
144 dir = tfs_mknod(parent_dir, dname, TFS_DIR);
145 if (IS_ERR(dir)) {
146 free_inode(parent_dir);
147 return PTR_ERR(dir);
150 err = tfs_add_entry(dir, ".", dir->i_ino, &dirty);
151 if (err)
152 goto out;
154 err = tfs_add_entry(dir, "..", parent_dir->i_ino, &dirty);
155 if (err < 0)
156 goto out;
158 if (dirty)
159 err = tfs_iwrite(dir);
160 out:
161 free_inode(dir);
162 free_inode(parent_dir);
164 return err;
168 * Check if the dir is empty or not.
170 static int is_empty_dir(struct inode *dir)
172 if (dir->i_size > 2 * sizeof(struct tfs_dir_entry))
173 return 0;
174 else if (dir->i_size < 2 * sizeof(struct tfs_dir_entry))
175 return -1;
176 else
177 return 1;
181 int tfs_rmdir(struct inode *dir, const char *dname)
183 struct inode *inode;
184 struct cache_struct *cs;
185 struct tfs_dir_entry *de;
186 struct tfs_sb_info *sbi = TFS_SBI(dir->i_fs);
187 int err;
190 cs = tfs_find_entry(dir, dname, &de);
191 if (IS_ERR_OR_NULL(cs)) {
192 err = cs ? PTR_ERR(cs) : -ENOENT;
193 goto out;
196 inode = tfs_iget_by_inr(dir->i_fs, de->d_inode);
197 if (IS_ERR(inode)) {
198 err = PTR_ERR(inode);
199 goto out;
202 if (inode->i_mode != TFS_DIR) {
203 err = -ENOTDIR;
204 goto error;
207 err = is_empty_dir(inode);
208 if (err == 0) {
209 err = -ENOTEMPTY;
210 goto error;
211 } else if (err == -1) {
212 printk("%s: path correupted: the size is less than two direntry!\n", dname);
213 err = -EINVAL;
214 goto error;
217 dir->i_size -= sizeof(struct tfs_dir_entry);
218 err = tfs_iwrite(dir);
219 if (err)
220 goto error;
221 de->d_inode = 0;
222 err = tfs_bwrite(sbi, cs->block, cs->data);
223 if (err)
224 goto error;
225 err = tfs_release_inode(sbi, inode);
226 goto out;
228 error:
229 free_inode(inode);
230 out:
231 return err;
235 int tfs_unlink(struct inode *dir, const char *dname)
237 struct inode *inode;
238 struct cache_struct *cs;
239 struct tfs_dir_entry *de;
240 struct tfs_sb_info *sbi = TFS_SBI(dir->i_fs);
241 int err;
243 cs = tfs_find_entry(dir, dname, &de);
244 if (IS_ERR_OR_NULL(cs)) {
245 err = cs ? PTR_ERR(cs) : -ENOENT;
246 goto out;
249 inode = tfs_iget_by_inr(dir->i_fs, de->d_inode);
250 if (IS_ERR(inode)) {
251 err = PTR_ERR(inode);
252 goto out;
255 if (inode->i_mode != TFS_FILE) {
256 printk("%s: not a file!\n", dname);
257 goto error;
260 dir->i_size -= sizeof(struct tfs_dir_entry);
261 err = tfs_iwrite(dir);
262 if (err)
263 goto error;
264 de->d_inode = 0;
265 err = tfs_bwrite(sbi, cs->block, cs->data);
266 if (err)
267 goto error;
268 err = tfs_release_inode(sbi, inode);
269 goto out;
271 error:
272 free_inode(inode);
273 out:
274 return err;
278 /* read one directry entry at a time */
279 struct dirent * tfs_readdir(struct file *file)
281 struct dirent *dirent;
282 struct tfs_dir_entry *de;
283 struct cache_struct *cs;
284 struct inode *inode = file->inode;
285 struct tfs_sb_info *sbi = TFS_SBI(file->fs);
286 int index = file->offset >> sbi->s_block_shift;
287 uint32_t block;
289 if (!(block = tfs_bmap(inode, index)))
290 return NULL;
291 cs = get_cache_block(file->fs, block);
292 de = (struct tfs_dir_entry *)(cs->data + (file->offset & (sbi->s_block_size- 1)));
294 if (!(dirent = malloc(sizeof(*dirent)))) {
295 printk("malloc dirent structure in tfs_readdir error!\n");
296 return NULL;
298 memset(dirent, 0, sizeof(*dirent));
299 dirent->d_ino = de->d_inode;
300 dirent->d_off = file->offset;
301 dirent->d_reclen = sizeof(struct tfs_dir_entry);
302 dirent->d_type = 0;
303 memcpy(dirent->d_name, de->d_name, TFS_NAME_LEN);
305 file->offset += sizeof(struct tfs_dir_entry);
307 /* Skip the invalid one */
308 if (de->d_inode == 0) {
309 free(dirent);
310 return tfs_readdir(file);
314 return dirent;
317 void tfs_closedir(DIR *dir)
319 if (dir) {
320 free_inode(dir->dd_dir->inode);
321 free(dir->dd_dir);
322 free(dir);