brintos

brintos / linux-shallow public Read only

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