xref: /openbmc/linux/fs/udf/dir.c (revision 5ce34554)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * dir.c
4  *
5  * PURPOSE
6  *  Directory handling routines for the OSTA-UDF(tm) filesystem.
7  *
8  * COPYRIGHT
9  *  (C) 1998-2004 Ben Fennema
10  *
11  * HISTORY
12  *
13  *  10/05/98 dgb  Split directory operations into its own file
14  *                Implemented directory reads via do_udf_readdir
15  *  10/06/98      Made directory operations work!
16  *  11/17/98      Rewrote directory to support ICBTAG_FLAG_AD_LONG
17  *  11/25/98 blf  Rewrote directory handling (readdir+lookup) to support reading
18  *                across blocks.
19  *  12/12/98      Split out the lookup code to namei.c. bulk of directory
20  *                code now in directory.c:udf_fileident_read.
21  */
22 
23 #include "udfdecl.h"
24 
25 #include <linux/string.h>
26 #include <linux/errno.h>
27 #include <linux/mm.h>
28 #include <linux/slab.h>
29 #include <linux/bio.h>
30 #include <linux/iversion.h>
31 
32 #include "udf_i.h"
33 #include "udf_sb.h"
34 
udf_readdir(struct file * file,struct dir_context * ctx)35 static int udf_readdir(struct file *file, struct dir_context *ctx)
36 {
37 	struct inode *dir = file_inode(file);
38 	loff_t nf_pos, emit_pos = 0;
39 	int flen;
40 	unsigned char *fname = NULL;
41 	int ret = 0;
42 	struct super_block *sb = dir->i_sb;
43 	bool pos_valid = false;
44 	struct udf_fileident_iter iter;
45 
46 	if (ctx->pos == 0) {
47 		if (!dir_emit_dot(file, ctx))
48 			return 0;
49 		ctx->pos = 1;
50 	}
51 	nf_pos = (ctx->pos - 1) << 2;
52 	if (nf_pos >= dir->i_size)
53 		goto out;
54 
55 	/*
56 	 * Something changed since last readdir (either lseek was called or dir
57 	 * changed)?  We need to verify the position correctly points at the
58 	 * beginning of some dir entry so that the directory parsing code does
59 	 * not get confused. Since UDF does not have any reliable way of
60 	 * identifying beginning of dir entry (names are under user control),
61 	 * we need to scan the directory from the beginning.
62 	 */
63 	if (!inode_eq_iversion(dir, file->f_version)) {
64 		emit_pos = nf_pos;
65 		nf_pos = 0;
66 	} else {
67 		pos_valid = true;
68 	}
69 
70 	fname = kmalloc(UDF_NAME_LEN, GFP_NOFS);
71 	if (!fname) {
72 		ret = -ENOMEM;
73 		goto out;
74 	}
75 
76 	for (ret = udf_fiiter_init(&iter, dir, nf_pos);
77 	     !ret && iter.pos < dir->i_size;
78 	     ret = udf_fiiter_advance(&iter)) {
79 		struct kernel_lb_addr tloc;
80 		udf_pblk_t iblock;
81 
82 		/* Still not at offset where user asked us to read from? */
83 		if (iter.pos < emit_pos)
84 			continue;
85 
86 		/* Update file position only if we got past the current one */
87 		pos_valid = true;
88 		ctx->pos = (iter.pos >> 2) + 1;
89 
90 		if (iter.fi.fileCharacteristics & FID_FILE_CHAR_DELETED) {
91 			if (!UDF_QUERY_FLAG(sb, UDF_FLAG_UNDELETE))
92 				continue;
93 		}
94 
95 		if (iter.fi.fileCharacteristics & FID_FILE_CHAR_HIDDEN) {
96 			if (!UDF_QUERY_FLAG(sb, UDF_FLAG_UNHIDE))
97 				continue;
98 		}
99 
100 		if (iter.fi.fileCharacteristics & FID_FILE_CHAR_PARENT) {
101 			if (!dir_emit_dotdot(file, ctx))
102 				goto out_iter;
103 			continue;
104 		}
105 
106 		flen = udf_get_filename(sb, iter.name,
107 				iter.fi.lengthFileIdent, fname, UDF_NAME_LEN);
108 		if (flen < 0)
109 			continue;
110 
111 		tloc = lelb_to_cpu(iter.fi.icb.extLocation);
112 		iblock = udf_get_lb_pblock(sb, &tloc, 0);
113 		if (!dir_emit(ctx, fname, flen, iblock, DT_UNKNOWN))
114 			goto out_iter;
115 	}
116 
117 	if (!ret) {
118 		ctx->pos = (iter.pos >> 2) + 1;
119 		pos_valid = true;
120 	}
121 out_iter:
122 	udf_fiiter_release(&iter);
123 out:
124 	if (pos_valid)
125 		file->f_version = inode_query_iversion(dir);
126 	kfree(fname);
127 
128 	return ret;
129 }
130 
131 /* readdir and lookup functions */
132 const struct file_operations udf_dir_operations = {
133 	.llseek			= generic_file_llseek,
134 	.read			= generic_read_dir,
135 	.iterate_shared		= udf_readdir,
136 	.unlocked_ioctl		= udf_ioctl,
137 	.fsync			= generic_file_fsync,
138 };
139