1// SPDX-License-Identifier: GPL-2.0
2/*
3 * QNX4 file system, Linux implementation.
4 *
5 * Version : 0.2.1
6 *
7 * Using parts of the xiafs filesystem.
8 *
9 * History :
10 *
11 * 01-06-1998 by Richard Frowijn : first release.
12 * 21-06-1998 by Frank Denis : dcache support, fixed error codes.
13 * 04-07-1998 by Frank Denis : first step for rmdir/unlink.
14 */
15
16#include <linux/buffer_head.h>
17#include "qnx4.h"
18
19
20/*
21 * check if the filename is correct. For some obscure reason, qnx writes a
22 * new file twice in the directory entry, first with all possible options at 0
23 * and for a second time the way it is, they want us not to access the qnx
24 * filesystem when whe are using linux.
25 */
26static int qnx4_match(int len, const char *name,
27		      struct buffer_head *bh, unsigned long *offset)
28{
29	union qnx4_directory_entry *de;
30	const char *fname;
31	int fnamelen;
32
33	if (bh == NULL) {
34		printk(KERN_WARNING "qnx4: matching unassigned buffer !\n");
35		return 0;
36	}
37	de = (union qnx4_directory_entry *) (bh->b_data + *offset);
38	*offset += QNX4_DIR_ENTRY_SIZE;
39
40	fname = get_entry_fname(de, &fnamelen);
41	if (!fname || len != fnamelen)
42		return 0;
43
44	if (strncmp(name, fname, len) == 0)
45		return 1;
46
47	return 0;
48}
49
50static struct buffer_head *qnx4_find_entry(int len, struct inode *dir,
51	   const char *name, struct qnx4_inode_entry **res_dir, int *ino)
52{
53	unsigned long block, offset, blkofs;
54	struct buffer_head *bh;
55
56	*res_dir = NULL;
57	bh = NULL;
58	block = offset = blkofs = 0;
59	while (blkofs * QNX4_BLOCK_SIZE + offset < dir->i_size) {
60		if (!bh) {
61			block = qnx4_block_map(dir, blkofs);
62			if (block)
63				bh = sb_bread(dir->i_sb, block);
64			if (!bh) {
65				blkofs++;
66				continue;
67			}
68		}
69		*res_dir = (struct qnx4_inode_entry *) (bh->b_data + offset);
70		if (qnx4_match(len, name, bh, &offset)) {
71			*ino = block * QNX4_INODES_PER_BLOCK +
72			    (offset / QNX4_DIR_ENTRY_SIZE) - 1;
73			return bh;
74		}
75		if (offset < bh->b_size) {
76			continue;
77		}
78		brelse(bh);
79		bh = NULL;
80		offset = 0;
81		blkofs++;
82	}
83	brelse(bh);
84	*res_dir = NULL;
85	return NULL;
86}
87
88struct dentry * qnx4_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
89{
90	int ino;
91	struct qnx4_inode_entry *de;
92	struct qnx4_link_info *lnk;
93	struct buffer_head *bh;
94	const char *name = dentry->d_name.name;
95	int len = dentry->d_name.len;
96	struct inode *foundinode = NULL;
97
98	if (!(bh = qnx4_find_entry(len, dir, name, &de, &ino)))
99		goto out;
100	/* The entry is linked, let's get the real info */
101	if ((de->di_status & QNX4_FILE_LINK) == QNX4_FILE_LINK) {
102		lnk = (struct qnx4_link_info *) de;
103		ino = (le32_to_cpu(lnk->dl_inode_blk) - 1) *
104                    QNX4_INODES_PER_BLOCK +
105		    lnk->dl_inode_ndx;
106	}
107	brelse(bh);
108
109	foundinode = qnx4_iget(dir->i_sb, ino);
110	if (IS_ERR(foundinode))
111		QNX4DEBUG((KERN_ERR "qnx4: lookup->iget -> error %ld\n",
112			   PTR_ERR(foundinode)));
113out:
114	return d_splice_alias(foundinode, dentry);
115}
116