GNU Linux-libre 4.9.308-gnu1
[releases.git] / fs / qnx6 / dir.c
1 /*
2  * QNX6 file system, Linux implementation.
3  *
4  * Version : 1.0.0
5  *
6  * History :
7  *
8  * 01-02-2012 by Kai Bankett (chaosman@ontika.net) : first release.
9  * 16-02-2012 pagemap extension by Al Viro
10  *
11  */
12
13 #include "qnx6.h"
14
15 static unsigned qnx6_lfile_checksum(char *name, unsigned size)
16 {
17         unsigned crc = 0;
18         char *end = name + size;
19         while (name < end) {
20                 crc = ((crc >> 1) + *(name++)) ^
21                         ((crc & 0x00000001) ? 0x80000000 : 0);
22         }
23         return crc;
24 }
25
26 static struct page *qnx6_get_page(struct inode *dir, unsigned long n)
27 {
28         struct address_space *mapping = dir->i_mapping;
29         struct page *page = read_mapping_page(mapping, n, NULL);
30         if (!IS_ERR(page))
31                 kmap(page);
32         return page;
33 }
34
35 static unsigned last_entry(struct inode *inode, unsigned long page_nr)
36 {
37         unsigned long last_byte = inode->i_size;
38         last_byte -= page_nr << PAGE_SHIFT;
39         if (last_byte > PAGE_SIZE)
40                 last_byte = PAGE_SIZE;
41         return last_byte / QNX6_DIR_ENTRY_SIZE;
42 }
43
44 static struct qnx6_long_filename *qnx6_longname(struct super_block *sb,
45                                          struct qnx6_long_dir_entry *de,
46                                          struct page **p)
47 {
48         struct qnx6_sb_info *sbi = QNX6_SB(sb);
49         u32 s = fs32_to_cpu(sbi, de->de_long_inode); /* in block units */
50         u32 n = s >> (PAGE_SHIFT - sb->s_blocksize_bits); /* in pages */
51         /* within page */
52         u32 offs = (s << sb->s_blocksize_bits) & ~PAGE_MASK;
53         struct address_space *mapping = sbi->longfile->i_mapping;
54         struct page *page = read_mapping_page(mapping, n, NULL);
55         if (IS_ERR(page))
56                 return ERR_CAST(page);
57         kmap(*p = page);
58         return (struct qnx6_long_filename *)(page_address(page) + offs);
59 }
60
61 static int qnx6_dir_longfilename(struct inode *inode,
62                         struct qnx6_long_dir_entry *de,
63                         struct dir_context *ctx,
64                         unsigned de_inode)
65 {
66         struct qnx6_long_filename *lf;
67         struct super_block *s = inode->i_sb;
68         struct qnx6_sb_info *sbi = QNX6_SB(s);
69         struct page *page;
70         int lf_size;
71
72         if (de->de_size != 0xff) {
73                 /* error - long filename entries always have size 0xff
74                    in direntry */
75                 pr_err("invalid direntry size (%i).\n", de->de_size);
76                 return 0;
77         }
78         lf = qnx6_longname(s, de, &page);
79         if (IS_ERR(lf)) {
80                 pr_err("Error reading longname\n");
81                 return 0;
82         }
83
84         lf_size = fs16_to_cpu(sbi, lf->lf_size);
85
86         if (lf_size > QNX6_LONG_NAME_MAX) {
87                 pr_debug("file %s\n", lf->lf_fname);
88                 pr_err("Filename too long (%i)\n", lf_size);
89                 qnx6_put_page(page);
90                 return 0;
91         }
92
93         /* calc & validate longfilename checksum
94            mmi 3g filesystem does not have that checksum */
95         if (!test_opt(s, MMI_FS) && fs32_to_cpu(sbi, de->de_checksum) !=
96                         qnx6_lfile_checksum(lf->lf_fname, lf_size))
97                 pr_info("long filename checksum error.\n");
98
99         pr_debug("qnx6_readdir:%.*s inode:%u\n",
100                  lf_size, lf->lf_fname, de_inode);
101         if (!dir_emit(ctx, lf->lf_fname, lf_size, de_inode, DT_UNKNOWN)) {
102                 qnx6_put_page(page);
103                 return 0;
104         }
105
106         qnx6_put_page(page);
107         /* success */
108         return 1;
109 }
110
111 static int qnx6_readdir(struct file *file, struct dir_context *ctx)
112 {
113         struct inode *inode = file_inode(file);
114         struct super_block *s = inode->i_sb;
115         struct qnx6_sb_info *sbi = QNX6_SB(s);
116         loff_t pos = ctx->pos & ~(QNX6_DIR_ENTRY_SIZE - 1);
117         unsigned long npages = dir_pages(inode);
118         unsigned long n = pos >> PAGE_SHIFT;
119         unsigned start = (pos & ~PAGE_MASK) / QNX6_DIR_ENTRY_SIZE;
120         bool done = false;
121
122         ctx->pos = pos;
123         if (ctx->pos >= inode->i_size)
124                 return 0;
125
126         for ( ; !done && n < npages; n++, start = 0) {
127                 struct page *page = qnx6_get_page(inode, n);
128                 int limit = last_entry(inode, n);
129                 struct qnx6_dir_entry *de;
130                 int i = start;
131
132                 if (IS_ERR(page)) {
133                         pr_err("%s(): read failed\n", __func__);
134                         ctx->pos = (n + 1) << PAGE_SHIFT;
135                         return PTR_ERR(page);
136                 }
137                 de = ((struct qnx6_dir_entry *)page_address(page)) + start;
138                 for (; i < limit; i++, de++, ctx->pos += QNX6_DIR_ENTRY_SIZE) {
139                         int size = de->de_size;
140                         u32 no_inode = fs32_to_cpu(sbi, de->de_inode);
141
142                         if (!no_inode || !size)
143                                 continue;
144
145                         if (size > QNX6_SHORT_NAME_MAX) {
146                                 /* long filename detected
147                                    get the filename from long filename
148                                    structure / block */
149                                 if (!qnx6_dir_longfilename(inode,
150                                         (struct qnx6_long_dir_entry *)de,
151                                         ctx, no_inode)) {
152                                         done = true;
153                                         break;
154                                 }
155                         } else {
156                                 pr_debug("%s():%.*s inode:%u\n",
157                                          __func__, size, de->de_fname,
158                                          no_inode);
159                                 if (!dir_emit(ctx, de->de_fname, size,
160                                       no_inode, DT_UNKNOWN)) {
161                                         done = true;
162                                         break;
163                                 }
164                         }
165                 }
166                 qnx6_put_page(page);
167         }
168         return 0;
169 }
170
171 /*
172  * check if the long filename is correct.
173  */
174 static unsigned qnx6_long_match(int len, const char *name,
175                         struct qnx6_long_dir_entry *de, struct inode *dir)
176 {
177         struct super_block *s = dir->i_sb;
178         struct qnx6_sb_info *sbi = QNX6_SB(s);
179         struct page *page;
180         int thislen;
181         struct qnx6_long_filename *lf = qnx6_longname(s, de, &page);
182
183         if (IS_ERR(lf))
184                 return 0;
185
186         thislen = fs16_to_cpu(sbi, lf->lf_size);
187         if (len != thislen) {
188                 qnx6_put_page(page);
189                 return 0;
190         }
191         if (memcmp(name, lf->lf_fname, len) == 0) {
192                 qnx6_put_page(page);
193                 return fs32_to_cpu(sbi, de->de_inode);
194         }
195         qnx6_put_page(page);
196         return 0;
197 }
198
199 /*
200  * check if the filename is correct.
201  */
202 static unsigned qnx6_match(struct super_block *s, int len, const char *name,
203                         struct qnx6_dir_entry *de)
204 {
205         struct qnx6_sb_info *sbi = QNX6_SB(s);
206         if (memcmp(name, de->de_fname, len) == 0)
207                 return fs32_to_cpu(sbi, de->de_inode);
208         return 0;
209 }
210
211
212 unsigned qnx6_find_entry(int len, struct inode *dir, const char *name,
213                          struct page **res_page)
214 {
215         struct super_block *s = dir->i_sb;
216         struct qnx6_inode_info *ei = QNX6_I(dir);
217         struct page *page = NULL;
218         unsigned long start, n;
219         unsigned long npages = dir_pages(dir);
220         unsigned ino;
221         struct qnx6_dir_entry *de;
222         struct qnx6_long_dir_entry *lde;
223
224         *res_page = NULL;
225
226         if (npages == 0)
227                 return 0;
228         start = ei->i_dir_start_lookup;
229         if (start >= npages)
230                 start = 0;
231         n = start;
232
233         do {
234                 page = qnx6_get_page(dir, n);
235                 if (!IS_ERR(page)) {
236                         int limit = last_entry(dir, n);
237                         int i;
238
239                         de = (struct qnx6_dir_entry *)page_address(page);
240                         for (i = 0; i < limit; i++, de++) {
241                                 if (len <= QNX6_SHORT_NAME_MAX) {
242                                         /* short filename */
243                                         if (len != de->de_size)
244                                                 continue;
245                                         ino = qnx6_match(s, len, name, de);
246                                         if (ino)
247                                                 goto found;
248                                 } else if (de->de_size == 0xff) {
249                                         /* deal with long filename */
250                                         lde = (struct qnx6_long_dir_entry *)de;
251                                         ino = qnx6_long_match(len,
252                                                                 name, lde, dir);
253                                         if (ino)
254                                                 goto found;
255                                 } else
256                                         pr_err("undefined filename size in inode.\n");
257                         }
258                         qnx6_put_page(page);
259                 }
260
261                 if (++n >= npages)
262                         n = 0;
263         } while (n != start);
264         return 0;
265
266 found:
267         *res_page = page;
268         ei->i_dir_start_lookup = n;
269         return ino;
270 }
271
272 const struct file_operations qnx6_dir_operations = {
273         .llseek         = generic_file_llseek,
274         .read           = generic_read_dir,
275         .iterate_shared = qnx6_readdir,
276         .fsync          = generic_file_fsync,
277 };
278
279 const struct inode_operations qnx6_dir_inode_operations = {
280         .lookup         = qnx6_lookup,
281 };