1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * linux/fs/minix/dir.c |
4 | * |
5 | * Copyright (C) 1991, 1992 Linus Torvalds |
6 | * |
7 | * minix directory handling functions |
8 | * |
9 | * Updated to filesystem version 3 by Daniel Aragones |
10 | */ |
11 | |
12 | #include "minix.h" |
13 | #include <linux/buffer_head.h> |
14 | #include <linux/highmem.h> |
15 | #include <linux/swap.h> |
16 | |
17 | typedef struct minix_dir_entry minix_dirent; |
18 | typedef struct minix3_dir_entry minix3_dirent; |
19 | |
20 | static int minix_readdir(struct file *, struct dir_context *); |
21 | |
22 | const struct file_operations minix_dir_operations = { |
23 | .llseek = generic_file_llseek, |
24 | .read = generic_read_dir, |
25 | .iterate_shared = minix_readdir, |
26 | .fsync = generic_file_fsync, |
27 | }; |
28 | |
29 | /* |
30 | * Return the offset into page `page_nr' of the last valid |
31 | * byte in that page, plus one. |
32 | */ |
33 | static unsigned |
34 | minix_last_byte(struct inode *inode, unsigned long page_nr) |
35 | { |
36 | unsigned last_byte = PAGE_SIZE; |
37 | |
38 | if (page_nr == (inode->i_size >> PAGE_SHIFT)) |
39 | last_byte = inode->i_size & (PAGE_SIZE - 1); |
40 | return last_byte; |
41 | } |
42 | |
43 | static void dir_commit_chunk(struct page *page, loff_t pos, unsigned len) |
44 | { |
45 | struct address_space *mapping = page->mapping; |
46 | struct inode *dir = mapping->host; |
47 | |
48 | block_write_end(NULL, mapping, pos, len, len, page, NULL); |
49 | |
50 | if (pos+len > dir->i_size) { |
51 | i_size_write(inode: dir, i_size: pos+len); |
52 | mark_inode_dirty(inode: dir); |
53 | } |
54 | unlock_page(page); |
55 | } |
56 | |
57 | static int minix_handle_dirsync(struct inode *dir) |
58 | { |
59 | int err; |
60 | |
61 | err = filemap_write_and_wait(mapping: dir->i_mapping); |
62 | if (!err) |
63 | err = sync_inode_metadata(inode: dir, wait: 1); |
64 | return err; |
65 | } |
66 | |
67 | static void *dir_get_page(struct inode *dir, unsigned long n, struct page **p) |
68 | { |
69 | struct address_space *mapping = dir->i_mapping; |
70 | struct page *page = read_mapping_page(mapping, index: n, NULL); |
71 | if (IS_ERR(ptr: page)) |
72 | return ERR_CAST(ptr: page); |
73 | *p = page; |
74 | return kmap_local_page(page); |
75 | } |
76 | |
77 | static inline void *minix_next_entry(void *de, struct minix_sb_info *sbi) |
78 | { |
79 | return (void*)((char*)de + sbi->s_dirsize); |
80 | } |
81 | |
82 | static int minix_readdir(struct file *file, struct dir_context *ctx) |
83 | { |
84 | struct inode *inode = file_inode(f: file); |
85 | struct super_block *sb = inode->i_sb; |
86 | struct minix_sb_info *sbi = minix_sb(sb); |
87 | unsigned chunk_size = sbi->s_dirsize; |
88 | unsigned long npages = dir_pages(inode); |
89 | unsigned long pos = ctx->pos; |
90 | unsigned offset; |
91 | unsigned long n; |
92 | |
93 | ctx->pos = pos = ALIGN(pos, chunk_size); |
94 | if (pos >= inode->i_size) |
95 | return 0; |
96 | |
97 | offset = pos & ~PAGE_MASK; |
98 | n = pos >> PAGE_SHIFT; |
99 | |
100 | for ( ; n < npages; n++, offset = 0) { |
101 | char *p, *kaddr, *limit; |
102 | struct page *page; |
103 | |
104 | kaddr = dir_get_page(dir: inode, n, p: &page); |
105 | if (IS_ERR(ptr: kaddr)) |
106 | continue; |
107 | p = kaddr+offset; |
108 | limit = kaddr + minix_last_byte(inode, page_nr: n) - chunk_size; |
109 | for ( ; p <= limit; p = minix_next_entry(de: p, sbi)) { |
110 | const char *name; |
111 | __u32 inumber; |
112 | if (sbi->s_version == MINIX_V3) { |
113 | minix3_dirent *de3 = (minix3_dirent *)p; |
114 | name = de3->name; |
115 | inumber = de3->inode; |
116 | } else { |
117 | minix_dirent *de = (minix_dirent *)p; |
118 | name = de->name; |
119 | inumber = de->inode; |
120 | } |
121 | if (inumber) { |
122 | unsigned l = strnlen(p: name, maxlen: sbi->s_namelen); |
123 | if (!dir_emit(ctx, name, namelen: l, |
124 | ino: inumber, DT_UNKNOWN)) { |
125 | unmap_and_put_page(page, addr: p); |
126 | return 0; |
127 | } |
128 | } |
129 | ctx->pos += chunk_size; |
130 | } |
131 | unmap_and_put_page(page, addr: kaddr); |
132 | } |
133 | return 0; |
134 | } |
135 | |
136 | static inline int namecompare(int len, int maxlen, |
137 | const char * name, const char * buffer) |
138 | { |
139 | if (len < maxlen && buffer[len]) |
140 | return 0; |
141 | return !memcmp(p: name, q: buffer, size: len); |
142 | } |
143 | |
144 | /* |
145 | * minix_find_entry() |
146 | * |
147 | * finds an entry in the specified directory with the wanted name. It |
148 | * returns the cache buffer in which the entry was found, and the entry |
149 | * itself (as a parameter - res_dir). It does NOT read the inode of the |
150 | * entry - you'll have to do that yourself if you want to. |
151 | */ |
152 | minix_dirent *minix_find_entry(struct dentry *dentry, struct page **res_page) |
153 | { |
154 | const char * name = dentry->d_name.name; |
155 | int namelen = dentry->d_name.len; |
156 | struct inode * dir = d_inode(dentry: dentry->d_parent); |
157 | struct super_block * sb = dir->i_sb; |
158 | struct minix_sb_info * sbi = minix_sb(sb); |
159 | unsigned long n; |
160 | unsigned long npages = dir_pages(inode: dir); |
161 | struct page *page = NULL; |
162 | char *p; |
163 | |
164 | char *namx; |
165 | __u32 inumber; |
166 | *res_page = NULL; |
167 | |
168 | for (n = 0; n < npages; n++) { |
169 | char *kaddr, *limit; |
170 | |
171 | kaddr = dir_get_page(dir, n, p: &page); |
172 | if (IS_ERR(ptr: kaddr)) |
173 | continue; |
174 | |
175 | limit = kaddr + minix_last_byte(inode: dir, page_nr: n) - sbi->s_dirsize; |
176 | for (p = kaddr; p <= limit; p = minix_next_entry(de: p, sbi)) { |
177 | if (sbi->s_version == MINIX_V3) { |
178 | minix3_dirent *de3 = (minix3_dirent *)p; |
179 | namx = de3->name; |
180 | inumber = de3->inode; |
181 | } else { |
182 | minix_dirent *de = (minix_dirent *)p; |
183 | namx = de->name; |
184 | inumber = de->inode; |
185 | } |
186 | if (!inumber) |
187 | continue; |
188 | if (namecompare(len: namelen, maxlen: sbi->s_namelen, name, buffer: namx)) |
189 | goto found; |
190 | } |
191 | unmap_and_put_page(page, addr: kaddr); |
192 | } |
193 | return NULL; |
194 | |
195 | found: |
196 | *res_page = page; |
197 | return (minix_dirent *)p; |
198 | } |
199 | |
200 | int minix_add_link(struct dentry *dentry, struct inode *inode) |
201 | { |
202 | struct inode *dir = d_inode(dentry: dentry->d_parent); |
203 | const char * name = dentry->d_name.name; |
204 | int namelen = dentry->d_name.len; |
205 | struct super_block * sb = dir->i_sb; |
206 | struct minix_sb_info * sbi = minix_sb(sb); |
207 | struct page *page = NULL; |
208 | unsigned long npages = dir_pages(inode: dir); |
209 | unsigned long n; |
210 | char *kaddr, *p; |
211 | minix_dirent *de; |
212 | minix3_dirent *de3; |
213 | loff_t pos; |
214 | int err; |
215 | char *namx = NULL; |
216 | __u32 inumber; |
217 | |
218 | /* |
219 | * We take care of directory expansion in the same loop |
220 | * This code plays outside i_size, so it locks the page |
221 | * to protect that region. |
222 | */ |
223 | for (n = 0; n <= npages; n++) { |
224 | char *limit, *dir_end; |
225 | |
226 | kaddr = dir_get_page(dir, n, p: &page); |
227 | if (IS_ERR(ptr: kaddr)) |
228 | return PTR_ERR(ptr: kaddr); |
229 | lock_page(page); |
230 | dir_end = kaddr + minix_last_byte(inode: dir, page_nr: n); |
231 | limit = kaddr + PAGE_SIZE - sbi->s_dirsize; |
232 | for (p = kaddr; p <= limit; p = minix_next_entry(de: p, sbi)) { |
233 | de = (minix_dirent *)p; |
234 | de3 = (minix3_dirent *)p; |
235 | if (sbi->s_version == MINIX_V3) { |
236 | namx = de3->name; |
237 | inumber = de3->inode; |
238 | } else { |
239 | namx = de->name; |
240 | inumber = de->inode; |
241 | } |
242 | if (p == dir_end) { |
243 | /* We hit i_size */ |
244 | if (sbi->s_version == MINIX_V3) |
245 | de3->inode = 0; |
246 | else |
247 | de->inode = 0; |
248 | goto got_it; |
249 | } |
250 | if (!inumber) |
251 | goto got_it; |
252 | err = -EEXIST; |
253 | if (namecompare(len: namelen, maxlen: sbi->s_namelen, name, buffer: namx)) |
254 | goto out_unlock; |
255 | } |
256 | unlock_page(page); |
257 | unmap_and_put_page(page, addr: kaddr); |
258 | } |
259 | BUG(); |
260 | return -EINVAL; |
261 | |
262 | got_it: |
263 | pos = page_offset(page) + offset_in_page(p); |
264 | err = minix_prepare_chunk(page, pos, len: sbi->s_dirsize); |
265 | if (err) |
266 | goto out_unlock; |
267 | memcpy (namx, name, namelen); |
268 | if (sbi->s_version == MINIX_V3) { |
269 | memset (namx + namelen, 0, sbi->s_dirsize - namelen - 4); |
270 | de3->inode = inode->i_ino; |
271 | } else { |
272 | memset (namx + namelen, 0, sbi->s_dirsize - namelen - 2); |
273 | de->inode = inode->i_ino; |
274 | } |
275 | dir_commit_chunk(page, pos, len: sbi->s_dirsize); |
276 | inode_set_mtime_to_ts(inode: dir, ts: inode_set_ctime_current(inode: dir)); |
277 | mark_inode_dirty(inode: dir); |
278 | err = minix_handle_dirsync(dir); |
279 | out_put: |
280 | unmap_and_put_page(page, addr: kaddr); |
281 | return err; |
282 | out_unlock: |
283 | unlock_page(page); |
284 | goto out_put; |
285 | } |
286 | |
287 | int minix_delete_entry(struct minix_dir_entry *de, struct page *page) |
288 | { |
289 | struct inode *inode = page->mapping->host; |
290 | loff_t pos = page_offset(page) + offset_in_page(de); |
291 | struct minix_sb_info *sbi = minix_sb(sb: inode->i_sb); |
292 | unsigned len = sbi->s_dirsize; |
293 | int err; |
294 | |
295 | lock_page(page); |
296 | err = minix_prepare_chunk(page, pos, len); |
297 | if (err) { |
298 | unlock_page(page); |
299 | return err; |
300 | } |
301 | if (sbi->s_version == MINIX_V3) |
302 | ((minix3_dirent *)de)->inode = 0; |
303 | else |
304 | de->inode = 0; |
305 | dir_commit_chunk(page, pos, len); |
306 | inode_set_mtime_to_ts(inode, ts: inode_set_ctime_current(inode)); |
307 | mark_inode_dirty(inode); |
308 | return minix_handle_dirsync(dir: inode); |
309 | } |
310 | |
311 | int minix_make_empty(struct inode *inode, struct inode *dir) |
312 | { |
313 | struct page *page = grab_cache_page(mapping: inode->i_mapping, index: 0); |
314 | struct minix_sb_info *sbi = minix_sb(sb: inode->i_sb); |
315 | char *kaddr; |
316 | int err; |
317 | |
318 | if (!page) |
319 | return -ENOMEM; |
320 | err = minix_prepare_chunk(page, pos: 0, len: 2 * sbi->s_dirsize); |
321 | if (err) { |
322 | unlock_page(page); |
323 | goto fail; |
324 | } |
325 | |
326 | kaddr = kmap_local_page(page); |
327 | memset(kaddr, 0, PAGE_SIZE); |
328 | |
329 | if (sbi->s_version == MINIX_V3) { |
330 | minix3_dirent *de3 = (minix3_dirent *)kaddr; |
331 | |
332 | de3->inode = inode->i_ino; |
333 | strcpy(p: de3->name, q: "." ); |
334 | de3 = minix_next_entry(de: de3, sbi); |
335 | de3->inode = dir->i_ino; |
336 | strcpy(p: de3->name, q: ".." ); |
337 | } else { |
338 | minix_dirent *de = (minix_dirent *)kaddr; |
339 | |
340 | de->inode = inode->i_ino; |
341 | strcpy(p: de->name, q: "." ); |
342 | de = minix_next_entry(de, sbi); |
343 | de->inode = dir->i_ino; |
344 | strcpy(p: de->name, q: ".." ); |
345 | } |
346 | kunmap_local(kaddr); |
347 | |
348 | dir_commit_chunk(page, pos: 0, len: 2 * sbi->s_dirsize); |
349 | err = minix_handle_dirsync(dir: inode); |
350 | fail: |
351 | put_page(page); |
352 | return err; |
353 | } |
354 | |
355 | /* |
356 | * routine to check that the specified directory is empty (for rmdir) |
357 | */ |
358 | int minix_empty_dir(struct inode * inode) |
359 | { |
360 | struct page *page = NULL; |
361 | unsigned long i, npages = dir_pages(inode); |
362 | struct minix_sb_info *sbi = minix_sb(sb: inode->i_sb); |
363 | char *name, *kaddr; |
364 | __u32 inumber; |
365 | |
366 | for (i = 0; i < npages; i++) { |
367 | char *p, *limit; |
368 | |
369 | kaddr = dir_get_page(dir: inode, n: i, p: &page); |
370 | if (IS_ERR(ptr: kaddr)) |
371 | continue; |
372 | |
373 | limit = kaddr + minix_last_byte(inode, page_nr: i) - sbi->s_dirsize; |
374 | for (p = kaddr; p <= limit; p = minix_next_entry(de: p, sbi)) { |
375 | if (sbi->s_version == MINIX_V3) { |
376 | minix3_dirent *de3 = (minix3_dirent *)p; |
377 | name = de3->name; |
378 | inumber = de3->inode; |
379 | } else { |
380 | minix_dirent *de = (minix_dirent *)p; |
381 | name = de->name; |
382 | inumber = de->inode; |
383 | } |
384 | |
385 | if (inumber != 0) { |
386 | /* check for . and .. */ |
387 | if (name[0] != '.') |
388 | goto not_empty; |
389 | if (!name[1]) { |
390 | if (inumber != inode->i_ino) |
391 | goto not_empty; |
392 | } else if (name[1] != '.') |
393 | goto not_empty; |
394 | else if (name[2]) |
395 | goto not_empty; |
396 | } |
397 | } |
398 | unmap_and_put_page(page, addr: kaddr); |
399 | } |
400 | return 1; |
401 | |
402 | not_empty: |
403 | unmap_and_put_page(page, addr: kaddr); |
404 | return 0; |
405 | } |
406 | |
407 | /* Releases the page */ |
408 | int minix_set_link(struct minix_dir_entry *de, struct page *page, |
409 | struct inode *inode) |
410 | { |
411 | struct inode *dir = page->mapping->host; |
412 | struct minix_sb_info *sbi = minix_sb(sb: dir->i_sb); |
413 | loff_t pos = page_offset(page) + offset_in_page(de); |
414 | int err; |
415 | |
416 | lock_page(page); |
417 | err = minix_prepare_chunk(page, pos, len: sbi->s_dirsize); |
418 | if (err) { |
419 | unlock_page(page); |
420 | return err; |
421 | } |
422 | if (sbi->s_version == MINIX_V3) |
423 | ((minix3_dirent *)de)->inode = inode->i_ino; |
424 | else |
425 | de->inode = inode->i_ino; |
426 | dir_commit_chunk(page, pos, len: sbi->s_dirsize); |
427 | inode_set_mtime_to_ts(inode: dir, ts: inode_set_ctime_current(inode: dir)); |
428 | mark_inode_dirty(inode: dir); |
429 | return minix_handle_dirsync(dir); |
430 | } |
431 | |
432 | struct minix_dir_entry * minix_dotdot (struct inode *dir, struct page **p) |
433 | { |
434 | struct minix_sb_info *sbi = minix_sb(sb: dir->i_sb); |
435 | struct minix_dir_entry *de = dir_get_page(dir, n: 0, p); |
436 | |
437 | if (!IS_ERR(ptr: de)) |
438 | return minix_next_entry(de, sbi); |
439 | return NULL; |
440 | } |
441 | |
442 | ino_t minix_inode_by_name(struct dentry *dentry) |
443 | { |
444 | struct page *page; |
445 | struct minix_dir_entry *de = minix_find_entry(dentry, res_page: &page); |
446 | ino_t res = 0; |
447 | |
448 | if (de) { |
449 | struct address_space *mapping = page->mapping; |
450 | struct inode *inode = mapping->host; |
451 | struct minix_sb_info *sbi = minix_sb(sb: inode->i_sb); |
452 | |
453 | if (sbi->s_version == MINIX_V3) |
454 | res = ((minix3_dirent *) de)->inode; |
455 | else |
456 | res = de->inode; |
457 | unmap_and_put_page(page, addr: de); |
458 | } |
459 | return res; |
460 | } |
461 | |