2 * QNX6 file system, Linux implementation.
8 * 01-02-2012 by Kai Bankett (chaosman@ontika.net) : first release.
9 * 16-02-2012 pagemap extension by Al Viro
15 static unsigned qnx6_lfile_checksum(char *name
, unsigned size
)
18 char *end
= name
+ size
;
20 crc
= ((crc
>> 1) + *(name
++)) ^
21 ((crc
& 0x00000001) ? 0x80000000 : 0);
26 static struct page
*qnx6_get_page(struct inode
*dir
, unsigned long n
)
28 struct address_space
*mapping
= dir
->i_mapping
;
29 struct page
*page
= read_mapping_page(mapping
, n
, NULL
);
35 static inline unsigned long dir_pages(struct inode
*inode
)
37 return (inode
->i_size
+PAGE_CACHE_SIZE
-1)>>PAGE_CACHE_SHIFT
;
40 static unsigned last_entry(struct inode
*inode
, unsigned long page_nr
)
42 unsigned long last_byte
= inode
->i_size
;
43 last_byte
-= page_nr
<< PAGE_CACHE_SHIFT
;
44 if (last_byte
> PAGE_CACHE_SIZE
)
45 last_byte
= PAGE_CACHE_SIZE
;
46 return last_byte
/ QNX6_DIR_ENTRY_SIZE
;
49 static struct qnx6_long_filename
*qnx6_longname(struct super_block
*sb
,
50 struct qnx6_long_dir_entry
*de
,
53 struct qnx6_sb_info
*sbi
= QNX6_SB(sb
);
54 u32 s
= fs32_to_cpu(sbi
, de
->de_long_inode
); /* in block units */
55 u32 n
= s
>> (PAGE_CACHE_SHIFT
- sb
->s_blocksize_bits
); /* in pages */
57 u32 offs
= (s
<< sb
->s_blocksize_bits
) & ~PAGE_CACHE_MASK
;
58 struct address_space
*mapping
= sbi
->longfile
->i_mapping
;
59 struct page
*page
= read_mapping_page(mapping
, n
, NULL
);
61 return ERR_CAST(page
);
63 return (struct qnx6_long_filename
*)(page_address(page
) + offs
);
66 static int qnx6_dir_longfilename(struct inode
*inode
,
67 struct qnx6_long_dir_entry
*de
,
68 void *dirent
, loff_t pos
,
69 unsigned de_inode
, filldir_t filldir
)
71 struct qnx6_long_filename
*lf
;
72 struct super_block
*s
= inode
->i_sb
;
73 struct qnx6_sb_info
*sbi
= QNX6_SB(s
);
77 if (de
->de_size
!= 0xff) {
78 /* error - long filename entries always have size 0xff
80 printk(KERN_ERR
"qnx6: invalid direntry size (%i).\n",
84 lf
= qnx6_longname(s
, de
, &page
);
86 printk(KERN_ERR
"qnx6:Error reading longname\n");
90 lf_size
= fs16_to_cpu(sbi
, lf
->lf_size
);
92 if (lf_size
> QNX6_LONG_NAME_MAX
) {
93 QNX6DEBUG((KERN_INFO
"file %s\n", lf
->lf_fname
));
94 printk(KERN_ERR
"qnx6:Filename too long (%i)\n", lf_size
);
99 /* calc & validate longfilename checksum
100 mmi 3g filesystem does not have that checksum */
101 if (!test_opt(s
, MMI_FS
) && fs32_to_cpu(sbi
, de
->de_checksum
) !=
102 qnx6_lfile_checksum(lf
->lf_fname
, lf_size
))
103 printk(KERN_INFO
"qnx6: long filename checksum error.\n");
105 QNX6DEBUG((KERN_INFO
"qnx6_readdir:%.*s inode:%u\n",
106 lf_size
, lf
->lf_fname
, de_inode
));
107 if (filldir(dirent
, lf
->lf_fname
, lf_size
, pos
, de_inode
,
118 static int qnx6_readdir(struct file
*filp
, void *dirent
, filldir_t filldir
)
120 struct inode
*inode
= file_inode(filp
);
121 struct super_block
*s
= inode
->i_sb
;
122 struct qnx6_sb_info
*sbi
= QNX6_SB(s
);
123 loff_t pos
= filp
->f_pos
& ~(QNX6_DIR_ENTRY_SIZE
- 1);
124 unsigned long npages
= dir_pages(inode
);
125 unsigned long n
= pos
>> PAGE_CACHE_SHIFT
;
126 unsigned start
= (pos
& ~PAGE_CACHE_MASK
) / QNX6_DIR_ENTRY_SIZE
;
129 if (filp
->f_pos
>= inode
->i_size
)
132 for ( ; !done
&& n
< npages
; n
++, start
= 0) {
133 struct page
*page
= qnx6_get_page(inode
, n
);
134 int limit
= last_entry(inode
, n
);
135 struct qnx6_dir_entry
*de
;
139 printk(KERN_ERR
"qnx6_readdir: read failed\n");
140 filp
->f_pos
= (n
+ 1) << PAGE_CACHE_SHIFT
;
141 return PTR_ERR(page
);
143 de
= ((struct qnx6_dir_entry
*)page_address(page
)) + start
;
144 for (; i
< limit
; i
++, de
++, pos
+= QNX6_DIR_ENTRY_SIZE
) {
145 int size
= de
->de_size
;
146 u32 no_inode
= fs32_to_cpu(sbi
, de
->de_inode
);
148 if (!no_inode
|| !size
)
151 if (size
> QNX6_SHORT_NAME_MAX
) {
152 /* long filename detected
153 get the filename from long filename
155 if (!qnx6_dir_longfilename(inode
,
156 (struct qnx6_long_dir_entry
*)de
,
157 dirent
, pos
, no_inode
,
163 QNX6DEBUG((KERN_INFO
"qnx6_readdir:%.*s"
164 " inode:%u\n", size
, de
->de_fname
,
166 if (filldir(dirent
, de
->de_fname
, size
,
167 pos
, no_inode
, DT_UNKNOWN
)
181 * check if the long filename is correct.
183 static unsigned qnx6_long_match(int len
, const char *name
,
184 struct qnx6_long_dir_entry
*de
, struct inode
*dir
)
186 struct super_block
*s
= dir
->i_sb
;
187 struct qnx6_sb_info
*sbi
= QNX6_SB(s
);
190 struct qnx6_long_filename
*lf
= qnx6_longname(s
, de
, &page
);
195 thislen
= fs16_to_cpu(sbi
, lf
->lf_size
);
196 if (len
!= thislen
) {
200 if (memcmp(name
, lf
->lf_fname
, len
) == 0) {
202 return fs32_to_cpu(sbi
, de
->de_inode
);
209 * check if the filename is correct.
211 static unsigned qnx6_match(struct super_block
*s
, int len
, const char *name
,
212 struct qnx6_dir_entry
*de
)
214 struct qnx6_sb_info
*sbi
= QNX6_SB(s
);
215 if (memcmp(name
, de
->de_fname
, len
) == 0)
216 return fs32_to_cpu(sbi
, de
->de_inode
);
221 unsigned qnx6_find_entry(int len
, struct inode
*dir
, const char *name
,
222 struct page
**res_page
)
224 struct super_block
*s
= dir
->i_sb
;
225 struct qnx6_inode_info
*ei
= QNX6_I(dir
);
226 struct page
*page
= NULL
;
227 unsigned long start
, n
;
228 unsigned long npages
= dir_pages(dir
);
230 struct qnx6_dir_entry
*de
;
231 struct qnx6_long_dir_entry
*lde
;
237 start
= ei
->i_dir_start_lookup
;
243 page
= qnx6_get_page(dir
, n
);
245 int limit
= last_entry(dir
, n
);
248 de
= (struct qnx6_dir_entry
*)page_address(page
);
249 for (i
= 0; i
< limit
; i
++, de
++) {
250 if (len
<= QNX6_SHORT_NAME_MAX
) {
252 if (len
!= de
->de_size
)
254 ino
= qnx6_match(s
, len
, name
, de
);
257 } else if (de
->de_size
== 0xff) {
258 /* deal with long filename */
259 lde
= (struct qnx6_long_dir_entry
*)de
;
260 ino
= qnx6_long_match(len
,
265 printk(KERN_ERR
"qnx6: undefined "
266 "filename size in inode.\n");
273 } while (n
!= start
);
278 ei
->i_dir_start_lookup
= n
;
282 const struct file_operations qnx6_dir_operations
= {
283 .llseek
= generic_file_llseek
,
284 .read
= generic_read_dir
,
285 .readdir
= qnx6_readdir
,
286 .fsync
= generic_file_fsync
,
289 const struct inode_operations qnx6_dir_inode_operations
= {
290 .lookup
= qnx6_lookup
,