1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * linux/fs/sysv/dir.c |
4 | * |
5 | * minix/dir.c |
6 | * Copyright (C) 1991, 1992 Linus Torvalds |
7 | * |
8 | * coh/dir.c |
9 | * Copyright (C) 1993 Pascal Haible, Bruno Haible |
10 | * |
11 | * sysv/dir.c |
12 | * Copyright (C) 1993 Bruno Haible |
13 | * |
14 | * SystemV/Coherent directory handling functions |
15 | */ |
16 | |
17 | #include <linux/pagemap.h> |
18 | #include <linux/highmem.h> |
19 | #include <linux/swap.h> |
20 | #include "sysv.h" |
21 | |
22 | static int sysv_readdir(struct file *, struct dir_context *); |
23 | |
24 | const struct file_operations sysv_dir_operations = { |
25 | .llseek = generic_file_llseek, |
26 | .read = generic_read_dir, |
27 | .iterate_shared = sysv_readdir, |
28 | .fsync = generic_file_fsync, |
29 | }; |
30 | |
31 | static void dir_commit_chunk(struct page *page, loff_t pos, unsigned len) |
32 | { |
33 | struct address_space *mapping = page->mapping; |
34 | struct inode *dir = mapping->host; |
35 | |
36 | block_write_end(NULL, mapping, pos, len, len, page, NULL); |
37 | if (pos+len > dir->i_size) { |
38 | i_size_write(inode: dir, i_size: pos+len); |
39 | mark_inode_dirty(inode: dir); |
40 | } |
41 | unlock_page(page); |
42 | } |
43 | |
44 | static int sysv_handle_dirsync(struct inode *dir) |
45 | { |
46 | int err; |
47 | |
48 | err = filemap_write_and_wait(mapping: dir->i_mapping); |
49 | if (!err) |
50 | err = sync_inode_metadata(inode: dir, wait: 1); |
51 | return err; |
52 | } |
53 | |
54 | /* |
55 | * Calls to dir_get_page()/unmap_and_put_page() must be nested according to the |
56 | * rules documented in mm/highmem.rst. |
57 | * |
58 | * NOTE: sysv_find_entry() and sysv_dotdot() act as calls to dir_get_page() |
59 | * and must be treated accordingly for nesting purposes. |
60 | */ |
61 | static void *dir_get_page(struct inode *dir, unsigned long n, struct page **p) |
62 | { |
63 | struct address_space *mapping = dir->i_mapping; |
64 | struct page *page = read_mapping_page(mapping, index: n, NULL); |
65 | if (IS_ERR(ptr: page)) |
66 | return ERR_CAST(ptr: page); |
67 | *p = page; |
68 | return kmap_local_page(page); |
69 | } |
70 | |
71 | static int sysv_readdir(struct file *file, struct dir_context *ctx) |
72 | { |
73 | unsigned long pos = ctx->pos; |
74 | struct inode *inode = file_inode(f: file); |
75 | struct super_block *sb = inode->i_sb; |
76 | unsigned long npages = dir_pages(inode); |
77 | unsigned offset; |
78 | unsigned long n; |
79 | |
80 | ctx->pos = pos = (pos + SYSV_DIRSIZE-1) & ~(SYSV_DIRSIZE-1); |
81 | if (pos >= inode->i_size) |
82 | return 0; |
83 | |
84 | offset = pos & ~PAGE_MASK; |
85 | n = pos >> PAGE_SHIFT; |
86 | |
87 | for ( ; n < npages; n++, offset = 0) { |
88 | char *kaddr, *limit; |
89 | struct sysv_dir_entry *de; |
90 | struct page *page; |
91 | |
92 | kaddr = dir_get_page(dir: inode, n, p: &page); |
93 | if (IS_ERR(ptr: kaddr)) |
94 | continue; |
95 | de = (struct sysv_dir_entry *)(kaddr+offset); |
96 | limit = kaddr + PAGE_SIZE - SYSV_DIRSIZE; |
97 | for ( ;(char*)de <= limit; de++, ctx->pos += sizeof(*de)) { |
98 | char *name = de->name; |
99 | |
100 | if (!de->inode) |
101 | continue; |
102 | |
103 | if (!dir_emit(ctx, name, namelen: strnlen(p: name,SYSV_NAMELEN), |
104 | ino: fs16_to_cpu(sbi: SYSV_SB(sb), n: de->inode), |
105 | DT_UNKNOWN)) { |
106 | unmap_and_put_page(page, addr: kaddr); |
107 | return 0; |
108 | } |
109 | } |
110 | unmap_and_put_page(page, addr: kaddr); |
111 | } |
112 | return 0; |
113 | } |
114 | |
115 | /* compare strings: name[0..len-1] (not zero-terminated) and |
116 | * buffer[0..] (filled with zeroes up to buffer[0..maxlen-1]) |
117 | */ |
118 | static inline int namecompare(int len, int maxlen, |
119 | const char * name, const char * buffer) |
120 | { |
121 | if (len < maxlen && buffer[len]) |
122 | return 0; |
123 | return !memcmp(p: name, q: buffer, size: len); |
124 | } |
125 | |
126 | /* |
127 | * sysv_find_entry() |
128 | * |
129 | * finds an entry in the specified directory with the wanted name. It |
130 | * returns the cache buffer in which the entry was found, and the entry |
131 | * itself (as a parameter - res_dir). It does NOT read the inode of the |
132 | * entry - you'll have to do that yourself if you want to. |
133 | * |
134 | * On Success unmap_and_put_page() should be called on *res_page. |
135 | * |
136 | * sysv_find_entry() acts as a call to dir_get_page() and must be treated |
137 | * accordingly for nesting purposes. |
138 | */ |
139 | struct sysv_dir_entry *sysv_find_entry(struct dentry *dentry, struct page **res_page) |
140 | { |
141 | const char * name = dentry->d_name.name; |
142 | int namelen = dentry->d_name.len; |
143 | struct inode * dir = d_inode(dentry: dentry->d_parent); |
144 | unsigned long start, n; |
145 | unsigned long npages = dir_pages(inode: dir); |
146 | struct page *page = NULL; |
147 | struct sysv_dir_entry *de; |
148 | |
149 | *res_page = NULL; |
150 | |
151 | start = SYSV_I(inode: dir)->i_dir_start_lookup; |
152 | if (start >= npages) |
153 | start = 0; |
154 | n = start; |
155 | |
156 | do { |
157 | char *kaddr = dir_get_page(dir, n, p: &page); |
158 | |
159 | if (!IS_ERR(ptr: kaddr)) { |
160 | de = (struct sysv_dir_entry *)kaddr; |
161 | kaddr += PAGE_SIZE - SYSV_DIRSIZE; |
162 | for ( ; (char *) de <= kaddr ; de++) { |
163 | if (!de->inode) |
164 | continue; |
165 | if (namecompare(len: namelen, SYSV_NAMELEN, |
166 | name, buffer: de->name)) |
167 | goto found; |
168 | } |
169 | unmap_and_put_page(page, addr: kaddr); |
170 | } |
171 | |
172 | if (++n >= npages) |
173 | n = 0; |
174 | } while (n != start); |
175 | |
176 | return NULL; |
177 | |
178 | found: |
179 | SYSV_I(inode: dir)->i_dir_start_lookup = n; |
180 | *res_page = page; |
181 | return de; |
182 | } |
183 | |
184 | int sysv_add_link(struct dentry *dentry, struct inode *inode) |
185 | { |
186 | struct inode *dir = d_inode(dentry: dentry->d_parent); |
187 | const char * name = dentry->d_name.name; |
188 | int namelen = dentry->d_name.len; |
189 | struct page *page = NULL; |
190 | struct sysv_dir_entry * de; |
191 | unsigned long npages = dir_pages(inode: dir); |
192 | unsigned long n; |
193 | char *kaddr; |
194 | loff_t pos; |
195 | int err; |
196 | |
197 | /* We take care of directory expansion in the same loop */ |
198 | for (n = 0; n <= npages; n++) { |
199 | kaddr = dir_get_page(dir, n, p: &page); |
200 | if (IS_ERR(ptr: kaddr)) |
201 | return PTR_ERR(ptr: kaddr); |
202 | de = (struct sysv_dir_entry *)kaddr; |
203 | kaddr += PAGE_SIZE - SYSV_DIRSIZE; |
204 | while ((char *)de <= kaddr) { |
205 | if (!de->inode) |
206 | goto got_it; |
207 | err = -EEXIST; |
208 | if (namecompare(len: namelen, SYSV_NAMELEN, name, buffer: de->name)) |
209 | goto out_page; |
210 | de++; |
211 | } |
212 | unmap_and_put_page(page, addr: kaddr); |
213 | } |
214 | BUG(); |
215 | return -EINVAL; |
216 | |
217 | got_it: |
218 | pos = page_offset(page) + offset_in_page(de); |
219 | lock_page(page); |
220 | err = sysv_prepare_chunk(page, pos, SYSV_DIRSIZE); |
221 | if (err) |
222 | goto out_unlock; |
223 | memcpy (de->name, name, namelen); |
224 | memset (de->name + namelen, 0, SYSV_DIRSIZE - namelen - 2); |
225 | de->inode = cpu_to_fs16(sbi: SYSV_SB(sb: inode->i_sb), n: inode->i_ino); |
226 | dir_commit_chunk(page, pos, SYSV_DIRSIZE); |
227 | inode_set_mtime_to_ts(inode: dir, ts: inode_set_ctime_current(inode: dir)); |
228 | mark_inode_dirty(inode: dir); |
229 | err = sysv_handle_dirsync(dir); |
230 | out_page: |
231 | unmap_and_put_page(page, addr: kaddr); |
232 | return err; |
233 | out_unlock: |
234 | unlock_page(page); |
235 | goto out_page; |
236 | } |
237 | |
238 | int sysv_delete_entry(struct sysv_dir_entry *de, struct page *page) |
239 | { |
240 | struct inode *inode = page->mapping->host; |
241 | loff_t pos = page_offset(page) + offset_in_page(de); |
242 | int err; |
243 | |
244 | lock_page(page); |
245 | err = sysv_prepare_chunk(page, pos, SYSV_DIRSIZE); |
246 | if (err) { |
247 | unlock_page(page); |
248 | return err; |
249 | } |
250 | de->inode = 0; |
251 | dir_commit_chunk(page, pos, SYSV_DIRSIZE); |
252 | inode_set_mtime_to_ts(inode, ts: inode_set_ctime_current(inode)); |
253 | mark_inode_dirty(inode); |
254 | return sysv_handle_dirsync(dir: inode); |
255 | } |
256 | |
257 | int sysv_make_empty(struct inode *inode, struct inode *dir) |
258 | { |
259 | struct page *page = grab_cache_page(mapping: inode->i_mapping, index: 0); |
260 | struct sysv_dir_entry * de; |
261 | char *base; |
262 | int err; |
263 | |
264 | if (!page) |
265 | return -ENOMEM; |
266 | err = sysv_prepare_chunk(page, pos: 0, len: 2 * SYSV_DIRSIZE); |
267 | if (err) { |
268 | unlock_page(page); |
269 | goto fail; |
270 | } |
271 | base = kmap_local_page(page); |
272 | memset(base, 0, PAGE_SIZE); |
273 | |
274 | de = (struct sysv_dir_entry *) base; |
275 | de->inode = cpu_to_fs16(sbi: SYSV_SB(sb: inode->i_sb), n: inode->i_ino); |
276 | strcpy(p: de->name,q: "." ); |
277 | de++; |
278 | de->inode = cpu_to_fs16(sbi: SYSV_SB(sb: inode->i_sb), n: dir->i_ino); |
279 | strcpy(p: de->name,q: ".." ); |
280 | |
281 | kunmap_local(base); |
282 | dir_commit_chunk(page, pos: 0, len: 2 * SYSV_DIRSIZE); |
283 | err = sysv_handle_dirsync(dir: inode); |
284 | fail: |
285 | put_page(page); |
286 | return err; |
287 | } |
288 | |
289 | /* |
290 | * routine to check that the specified directory is empty (for rmdir) |
291 | */ |
292 | int sysv_empty_dir(struct inode * inode) |
293 | { |
294 | struct super_block *sb = inode->i_sb; |
295 | struct page *page = NULL; |
296 | unsigned long i, npages = dir_pages(inode); |
297 | char *kaddr; |
298 | |
299 | for (i = 0; i < npages; i++) { |
300 | struct sysv_dir_entry *de; |
301 | |
302 | kaddr = dir_get_page(dir: inode, n: i, p: &page); |
303 | if (IS_ERR(ptr: kaddr)) |
304 | continue; |
305 | |
306 | de = (struct sysv_dir_entry *)kaddr; |
307 | kaddr += PAGE_SIZE-SYSV_DIRSIZE; |
308 | |
309 | for ( ;(char *)de <= kaddr; de++) { |
310 | if (!de->inode) |
311 | continue; |
312 | /* check for . and .. */ |
313 | if (de->name[0] != '.') |
314 | goto not_empty; |
315 | if (!de->name[1]) { |
316 | if (de->inode == cpu_to_fs16(sbi: SYSV_SB(sb), |
317 | n: inode->i_ino)) |
318 | continue; |
319 | goto not_empty; |
320 | } |
321 | if (de->name[1] != '.' || de->name[2]) |
322 | goto not_empty; |
323 | } |
324 | unmap_and_put_page(page, addr: kaddr); |
325 | } |
326 | return 1; |
327 | |
328 | not_empty: |
329 | unmap_and_put_page(page, addr: kaddr); |
330 | return 0; |
331 | } |
332 | |
333 | /* Releases the page */ |
334 | int sysv_set_link(struct sysv_dir_entry *de, struct page *page, |
335 | struct inode *inode) |
336 | { |
337 | struct inode *dir = page->mapping->host; |
338 | loff_t pos = page_offset(page) + offset_in_page(de); |
339 | int err; |
340 | |
341 | lock_page(page); |
342 | err = sysv_prepare_chunk(page, pos, SYSV_DIRSIZE); |
343 | if (err) { |
344 | unlock_page(page); |
345 | return err; |
346 | } |
347 | de->inode = cpu_to_fs16(sbi: SYSV_SB(sb: inode->i_sb), n: inode->i_ino); |
348 | dir_commit_chunk(page, pos, SYSV_DIRSIZE); |
349 | inode_set_mtime_to_ts(inode: dir, ts: inode_set_ctime_current(inode: dir)); |
350 | mark_inode_dirty(inode: dir); |
351 | return sysv_handle_dirsync(dir: inode); |
352 | } |
353 | |
354 | /* |
355 | * Calls to dir_get_page()/unmap_and_put_page() must be nested according to the |
356 | * rules documented in mm/highmem.rst. |
357 | * |
358 | * sysv_dotdot() acts as a call to dir_get_page() and must be treated |
359 | * accordingly for nesting purposes. |
360 | */ |
361 | struct sysv_dir_entry *sysv_dotdot(struct inode *dir, struct page **p) |
362 | { |
363 | struct sysv_dir_entry *de = dir_get_page(dir, n: 0, p); |
364 | |
365 | if (IS_ERR(ptr: de)) |
366 | return NULL; |
367 | /* ".." is the second directory entry */ |
368 | return de + 1; |
369 | } |
370 | |
371 | ino_t sysv_inode_by_name(struct dentry *dentry) |
372 | { |
373 | struct page *page; |
374 | struct sysv_dir_entry *de = sysv_find_entry (dentry, res_page: &page); |
375 | ino_t res = 0; |
376 | |
377 | if (de) { |
378 | res = fs16_to_cpu(sbi: SYSV_SB(sb: dentry->d_sb), n: de->inode); |
379 | unmap_and_put_page(page, addr: de); |
380 | } |
381 | return res; |
382 | } |
383 | |