Blame view

fs/qnx6/dir.c 6.75 KB
b24413180   Greg Kroah-Hartman   License cleanup: ...
1
  // SPDX-License-Identifier: GPL-2.0
5d026c724   Kai Bankett   fs: initial qnx6f...
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
  /*
   * QNX6 file system, Linux implementation.
   *
   * Version : 1.0.0
   *
   * History :
   *
   * 01-02-2012 by Kai Bankett (chaosman@ontika.net) : first release.
   * 16-02-2012 pagemap extension by Al Viro
   *
   */
  
  #include "qnx6.h"
  
  static unsigned qnx6_lfile_checksum(char *name, unsigned size)
  {
  	unsigned crc = 0;
  	char *end = name + size;
  	while (name < end) {
  		crc = ((crc >> 1) + *(name++)) ^
  			((crc & 0x00000001) ? 0x80000000 : 0);
  	}
  	return crc;
  }
  
  static struct page *qnx6_get_page(struct inode *dir, unsigned long n)
  {
  	struct address_space *mapping = dir->i_mapping;
  	struct page *page = read_mapping_page(mapping, n, NULL);
  	if (!IS_ERR(page))
  		kmap(page);
  	return page;
  }
5d026c724   Kai Bankett   fs: initial qnx6f...
35
36
37
  static unsigned last_entry(struct inode *inode, unsigned long page_nr)
  {
  	unsigned long last_byte = inode->i_size;
09cbfeaf1   Kirill A. Shutemov   mm, fs: get rid o...
38
39
40
  	last_byte -= page_nr << PAGE_SHIFT;
  	if (last_byte > PAGE_SIZE)
  		last_byte = PAGE_SIZE;
5d026c724   Kai Bankett   fs: initial qnx6f...
41
42
43
44
45
46
47
48
49
  	return last_byte / QNX6_DIR_ENTRY_SIZE;
  }
  
  static struct qnx6_long_filename *qnx6_longname(struct super_block *sb,
  					 struct qnx6_long_dir_entry *de,
  					 struct page **p)
  {
  	struct qnx6_sb_info *sbi = QNX6_SB(sb);
  	u32 s = fs32_to_cpu(sbi, de->de_long_inode); /* in block units */
09cbfeaf1   Kirill A. Shutemov   mm, fs: get rid o...
50
  	u32 n = s >> (PAGE_SHIFT - sb->s_blocksize_bits); /* in pages */
5d026c724   Kai Bankett   fs: initial qnx6f...
51
  	/* within page */
09cbfeaf1   Kirill A. Shutemov   mm, fs: get rid o...
52
  	u32 offs = (s << sb->s_blocksize_bits) & ~PAGE_MASK;
5d026c724   Kai Bankett   fs: initial qnx6f...
53
54
55
56
57
58
59
60
61
62
  	struct address_space *mapping = sbi->longfile->i_mapping;
  	struct page *page = read_mapping_page(mapping, n, NULL);
  	if (IS_ERR(page))
  		return ERR_CAST(page);
  	kmap(*p = page);
  	return (struct qnx6_long_filename *)(page_address(page) + offs);
  }
  
  static int qnx6_dir_longfilename(struct inode *inode,
  			struct qnx6_long_dir_entry *de,
4deb398a1   Al Viro   [readdir] convert...
63
64
  			struct dir_context *ctx,
  			unsigned de_inode)
5d026c724   Kai Bankett   fs: initial qnx6f...
65
66
67
68
69
70
71
72
73
74
  {
  	struct qnx6_long_filename *lf;
  	struct super_block *s = inode->i_sb;
  	struct qnx6_sb_info *sbi = QNX6_SB(s);
  	struct page *page;
  	int lf_size;
  
  	if (de->de_size != 0xff) {
  		/* error - long filename entries always have size 0xff
  		   in direntry */
e6c326165   Fabian Frederick   fs/qnx6: use pr_f...
75
76
  		pr_err("invalid direntry size (%i).
  ", de->de_size);
5d026c724   Kai Bankett   fs: initial qnx6f...
77
78
79
80
  		return 0;
  	}
  	lf = qnx6_longname(s, de, &page);
  	if (IS_ERR(lf)) {
e6c326165   Fabian Frederick   fs/qnx6: use pr_f...
81
82
  		pr_err("Error reading longname
  ");
5d026c724   Kai Bankett   fs: initial qnx6f...
83
84
85
86
87
88
  		return 0;
  	}
  
  	lf_size = fs16_to_cpu(sbi, lf->lf_size);
  
  	if (lf_size > QNX6_LONG_NAME_MAX) {
fa5a7a41a   Fabian Frederick   fs/qnx6: update d...
89
90
  		pr_debug("file %s
  ", lf->lf_fname);
e6c326165   Fabian Frederick   fs/qnx6: use pr_f...
91
92
  		pr_err("Filename too long (%i)
  ", lf_size);
5d026c724   Kai Bankett   fs: initial qnx6f...
93
94
95
96
97
98
99
100
  		qnx6_put_page(page);
  		return 0;
  	}
  
  	/* calc & validate longfilename checksum
  	   mmi 3g filesystem does not have that checksum */
  	if (!test_opt(s, MMI_FS) && fs32_to_cpu(sbi, de->de_checksum) !=
  			qnx6_lfile_checksum(lf->lf_fname, lf_size))
e6c326165   Fabian Frederick   fs/qnx6: use pr_f...
101
102
  		pr_info("long filename checksum error.
  ");
5d026c724   Kai Bankett   fs: initial qnx6f...
103

fa5a7a41a   Fabian Frederick   fs/qnx6: update d...
104
105
106
  	pr_debug("qnx6_readdir:%.*s inode:%u
  ",
  		 lf_size, lf->lf_fname, de_inode);
4deb398a1   Al Viro   [readdir] convert...
107
  	if (!dir_emit(ctx, lf->lf_fname, lf_size, de_inode, DT_UNKNOWN)) {
5d026c724   Kai Bankett   fs: initial qnx6f...
108
109
110
111
112
113
114
115
  		qnx6_put_page(page);
  		return 0;
  	}
  
  	qnx6_put_page(page);
  	/* success */
  	return 1;
  }
4deb398a1   Al Viro   [readdir] convert...
116
  static int qnx6_readdir(struct file *file, struct dir_context *ctx)
5d026c724   Kai Bankett   fs: initial qnx6f...
117
  {
4deb398a1   Al Viro   [readdir] convert...
118
  	struct inode *inode = file_inode(file);
5d026c724   Kai Bankett   fs: initial qnx6f...
119
120
  	struct super_block *s = inode->i_sb;
  	struct qnx6_sb_info *sbi = QNX6_SB(s);
4deb398a1   Al Viro   [readdir] convert...
121
  	loff_t pos = ctx->pos & ~(QNX6_DIR_ENTRY_SIZE - 1);
5d026c724   Kai Bankett   fs: initial qnx6f...
122
  	unsigned long npages = dir_pages(inode);
09cbfeaf1   Kirill A. Shutemov   mm, fs: get rid o...
123
124
  	unsigned long n = pos >> PAGE_SHIFT;
  	unsigned start = (pos & ~PAGE_MASK) / QNX6_DIR_ENTRY_SIZE;
5d026c724   Kai Bankett   fs: initial qnx6f...
125
  	bool done = false;
4deb398a1   Al Viro   [readdir] convert...
126
127
  	ctx->pos = pos;
  	if (ctx->pos >= inode->i_size)
5d026c724   Kai Bankett   fs: initial qnx6f...
128
129
130
131
132
133
134
135
136
  		return 0;
  
  	for ( ; !done && n < npages; n++, start = 0) {
  		struct page *page = qnx6_get_page(inode, n);
  		int limit = last_entry(inode, n);
  		struct qnx6_dir_entry *de;
  		int i = start;
  
  		if (IS_ERR(page)) {
e6c326165   Fabian Frederick   fs/qnx6: use pr_f...
137
138
  			pr_err("%s(): read failed
  ", __func__);
09cbfeaf1   Kirill A. Shutemov   mm, fs: get rid o...
139
  			ctx->pos = (n + 1) << PAGE_SHIFT;
5d026c724   Kai Bankett   fs: initial qnx6f...
140
141
142
  			return PTR_ERR(page);
  		}
  		de = ((struct qnx6_dir_entry *)page_address(page)) + start;
4deb398a1   Al Viro   [readdir] convert...
143
  		for (; i < limit; i++, de++, ctx->pos += QNX6_DIR_ENTRY_SIZE) {
5d026c724   Kai Bankett   fs: initial qnx6f...
144
145
146
147
148
149
150
151
152
153
154
155
  			int size = de->de_size;
  			u32 no_inode = fs32_to_cpu(sbi, de->de_inode);
  
  			if (!no_inode || !size)
  				continue;
  
  			if (size > QNX6_SHORT_NAME_MAX) {
  				/* long filename detected
  				   get the filename from long filename
  				   structure / block */
  				if (!qnx6_dir_longfilename(inode,
  					(struct qnx6_long_dir_entry *)de,
4deb398a1   Al Viro   [readdir] convert...
156
  					ctx, no_inode)) {
5d026c724   Kai Bankett   fs: initial qnx6f...
157
158
159
160
  					done = true;
  					break;
  				}
  			} else {
fa5a7a41a   Fabian Frederick   fs/qnx6: update d...
161
162
163
164
  				pr_debug("%s():%.*s inode:%u
  ",
  					 __func__, size, de->de_fname,
  					 no_inode);
4deb398a1   Al Viro   [readdir] convert...
165
166
  				if (!dir_emit(ctx, de->de_fname, size,
  				      no_inode, DT_UNKNOWN)) {
5d026c724   Kai Bankett   fs: initial qnx6f...
167
168
169
170
171
172
173
  					done = true;
  					break;
  				}
  			}
  		}
  		qnx6_put_page(page);
  	}
5d026c724   Kai Bankett   fs: initial qnx6f...
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
  	return 0;
  }
  
  /*
   * check if the long filename is correct.
   */
  static unsigned qnx6_long_match(int len, const char *name,
  			struct qnx6_long_dir_entry *de, struct inode *dir)
  {
  	struct super_block *s = dir->i_sb;
  	struct qnx6_sb_info *sbi = QNX6_SB(s);
  	struct page *page;
  	int thislen;
  	struct qnx6_long_filename *lf = qnx6_longname(s, de, &page);
  
  	if (IS_ERR(lf))
  		return 0;
  
  	thislen = fs16_to_cpu(sbi, lf->lf_size);
  	if (len != thislen) {
  		qnx6_put_page(page);
  		return 0;
  	}
  	if (memcmp(name, lf->lf_fname, len) == 0) {
  		qnx6_put_page(page);
  		return fs32_to_cpu(sbi, de->de_inode);
  	}
  	qnx6_put_page(page);
  	return 0;
  }
  
  /*
   * check if the filename is correct.
   */
  static unsigned qnx6_match(struct super_block *s, int len, const char *name,
  			struct qnx6_dir_entry *de)
  {
  	struct qnx6_sb_info *sbi = QNX6_SB(s);
  	if (memcmp(name, de->de_fname, len) == 0)
  		return fs32_to_cpu(sbi, de->de_inode);
  	return 0;
  }
  
  
  unsigned qnx6_find_entry(int len, struct inode *dir, const char *name,
  			 struct page **res_page)
  {
  	struct super_block *s = dir->i_sb;
  	struct qnx6_inode_info *ei = QNX6_I(dir);
  	struct page *page = NULL;
  	unsigned long start, n;
  	unsigned long npages = dir_pages(dir);
  	unsigned ino;
  	struct qnx6_dir_entry *de;
  	struct qnx6_long_dir_entry *lde;
  
  	*res_page = NULL;
  
  	if (npages == 0)
  		return 0;
  	start = ei->i_dir_start_lookup;
  	if (start >= npages)
  		start = 0;
  	n = start;
  
  	do {
  		page = qnx6_get_page(dir, n);
  		if (!IS_ERR(page)) {
  			int limit = last_entry(dir, n);
  			int i;
  
  			de = (struct qnx6_dir_entry *)page_address(page);
  			for (i = 0; i < limit; i++, de++) {
  				if (len <= QNX6_SHORT_NAME_MAX) {
  					/* short filename */
  					if (len != de->de_size)
  						continue;
  					ino = qnx6_match(s, len, name, de);
  					if (ino)
  						goto found;
  				} else if (de->de_size == 0xff) {
  					/* deal with long filename */
  					lde = (struct qnx6_long_dir_entry *)de;
  					ino = qnx6_long_match(len,
  								name, lde, dir);
  					if (ino)
  						goto found;
  				} else
e6c326165   Fabian Frederick   fs/qnx6: use pr_f...
262
263
  					pr_err("undefined filename size in inode.
  ");
5d026c724   Kai Bankett   fs: initial qnx6f...
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
  			}
  			qnx6_put_page(page);
  		}
  
  		if (++n >= npages)
  			n = 0;
  	} while (n != start);
  	return 0;
  
  found:
  	*res_page = page;
  	ei->i_dir_start_lookup = n;
  	return ino;
  }
  
  const struct file_operations qnx6_dir_operations = {
  	.llseek		= generic_file_llseek,
  	.read		= generic_read_dir,
c51da20c4   Al Viro   more trivial ->it...
282
  	.iterate_shared	= qnx6_readdir,
5d026c724   Kai Bankett   fs: initial qnx6f...
283
284
285
286
287
288
  	.fsync		= generic_file_fsync,
  };
  
  const struct inode_operations qnx6_dir_inode_operations = {
  	.lookup		= qnx6_lookup,
  };