1/*
2 * directory.c
3 *
4 * PURPOSE
5 *	Directory related functions
6 *
7 * COPYRIGHT
8 *	This file is distributed under the terms of the GNU General Public
9 *	License (GPL). Copies of the GPL can be obtained from:
10 *		ftp://prep.ai.mit.edu/pub/gnu/GPL
11 *	Each contributing author retains all rights to their own work.
12 */
13
14#include "udfdecl.h"
15#include "udf_i.h"
16
17#include <linux/fs.h>
18#include <linux/string.h>
19#include <linux/buffer_head.h>
20
21
22struct fileIdentDesc *
23udf_fileident_read(struct inode *dir, loff_t *nf_pos,
24	struct udf_fileident_bh *fibh,
25	struct fileIdentDesc *cfi,
26	struct extent_position *epos,
27	kernel_lb_addr *eloc, uint32_t *elen,
28	sector_t *offset)
29{
30	struct fileIdentDesc *fi;
31	int i, num, block;
32	struct buffer_head * tmp, * bha[16];
33
34	fibh->soffset = fibh->eoffset;
35
36	if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB)
37	{
38		fi = udf_get_fileident(UDF_I_DATA(dir) -
39			(UDF_I_EFE(dir) ?
40				sizeof(struct extendedFileEntry) :
41				sizeof(struct fileEntry)),
42			dir->i_sb->s_blocksize, &(fibh->eoffset));
43
44		if (!fi)
45			return NULL;
46
47		*nf_pos += ((fibh->eoffset - fibh->soffset) >> 2);
48
49		memcpy((uint8_t *)cfi, (uint8_t *)fi, sizeof(struct fileIdentDesc));
50
51		return fi;
52	}
53
54	if (fibh->eoffset == dir->i_sb->s_blocksize)
55	{
56		int lextoffset = epos->offset;
57
58		if (udf_next_aext(dir, epos, eloc, elen, 1) !=
59			(EXT_RECORDED_ALLOCATED >> 30))
60			return NULL;
61
62		block = udf_get_lb_pblock(dir->i_sb, *eloc, *offset);
63
64		(*offset) ++;
65
66		if ((*offset << dir->i_sb->s_blocksize_bits) >= *elen)
67			*offset = 0;
68		else
69			epos->offset = lextoffset;
70
71		brelse(fibh->sbh);
72		if (!(fibh->sbh = fibh->ebh = udf_tread(dir->i_sb, block)))
73			return NULL;
74		fibh->soffset = fibh->eoffset = 0;
75
76		if (!(*offset & ((16 >> (dir->i_sb->s_blocksize_bits - 9))-1)))
77		{
78			i = 16 >> (dir->i_sb->s_blocksize_bits - 9);
79			if (i+*offset > (*elen >> dir->i_sb->s_blocksize_bits))
80				i = (*elen >> dir->i_sb->s_blocksize_bits)-*offset;
81			for (num=0; i>0; i--)
82			{
83				block = udf_get_lb_pblock(dir->i_sb, *eloc, *offset+i);
84				tmp = udf_tgetblk(dir->i_sb, block);
85				if (tmp && !buffer_uptodate(tmp) && !buffer_locked(tmp))
86					bha[num++] = tmp;
87				else
88					brelse(tmp);
89			}
90			if (num)
91			{
92				ll_rw_block(READA, num, bha);
93				for (i=0; i<num; i++)
94					brelse(bha[i]);
95			}
96		}
97	}
98	else if (fibh->sbh != fibh->ebh)
99	{
100		brelse(fibh->sbh);
101		fibh->sbh = fibh->ebh;
102	}
103
104	fi = udf_get_fileident(fibh->sbh->b_data, dir->i_sb->s_blocksize,
105		&(fibh->eoffset));
106
107	if (!fi)
108		return NULL;
109
110	*nf_pos += ((fibh->eoffset - fibh->soffset) >> 2);
111
112	if (fibh->eoffset <= dir->i_sb->s_blocksize)
113	{
114		memcpy((uint8_t *)cfi, (uint8_t *)fi, sizeof(struct fileIdentDesc));
115	}
116	else if (fibh->eoffset > dir->i_sb->s_blocksize)
117	{
118		int lextoffset = epos->offset;
119
120		if (udf_next_aext(dir, epos, eloc, elen, 1) !=
121			(EXT_RECORDED_ALLOCATED >> 30))
122			return NULL;
123
124		block = udf_get_lb_pblock(dir->i_sb, *eloc, *offset);
125
126		(*offset) ++;
127
128		if ((*offset << dir->i_sb->s_blocksize_bits) >= *elen)
129			*offset = 0;
130		else
131			epos->offset = lextoffset;
132
133		fibh->soffset -= dir->i_sb->s_blocksize;
134		fibh->eoffset -= dir->i_sb->s_blocksize;
135
136		if (!(fibh->ebh = udf_tread(dir->i_sb, block)))
137			return NULL;
138
139		if (sizeof(struct fileIdentDesc) > - fibh->soffset)
140		{
141			int fi_len;
142
143			memcpy((uint8_t *)cfi, (uint8_t *)fi, - fibh->soffset);
144			memcpy((uint8_t *)cfi - fibh->soffset, fibh->ebh->b_data,
145				sizeof(struct fileIdentDesc) + fibh->soffset);
146
147			fi_len = (sizeof(struct fileIdentDesc) + cfi->lengthFileIdent +
148				le16_to_cpu(cfi->lengthOfImpUse) + 3) & ~3;
149
150			*nf_pos += ((fi_len - (fibh->eoffset - fibh->soffset)) >> 2);
151			fibh->eoffset = fibh->soffset + fi_len;
152		}
153		else
154		{
155			memcpy((uint8_t *)cfi, (uint8_t *)fi, sizeof(struct fileIdentDesc));
156		}
157	}
158	return fi;
159}
160
161struct fileIdentDesc *
162udf_get_fileident(void * buffer, int bufsize, int * offset)
163{
164	struct fileIdentDesc *fi;
165	int lengthThisIdent;
166	uint8_t * ptr;
167	int padlen;
168
169	if ( (!buffer) || (!offset) ) {
170		udf_debug("invalidparms\n, buffer=%p, offset=%p\n", buffer, offset);
171		return NULL;
172	}
173
174	ptr = buffer;
175
176	if ( (*offset > 0) && (*offset < bufsize) ) {
177		ptr += *offset;
178	}
179	fi=(struct fileIdentDesc *)ptr;
180	if (le16_to_cpu(fi->descTag.tagIdent) != TAG_IDENT_FID)
181	{
182		udf_debug("0x%x != TAG_IDENT_FID\n",
183			le16_to_cpu(fi->descTag.tagIdent));
184		udf_debug("offset: %u sizeof: %lu bufsize: %u\n",
185			*offset, (unsigned long)sizeof(struct fileIdentDesc), bufsize);
186		return NULL;
187	}
188	if ( (*offset + sizeof(struct fileIdentDesc)) > bufsize )
189	{
190		lengthThisIdent = sizeof(struct fileIdentDesc);
191	}
192	else
193		lengthThisIdent = sizeof(struct fileIdentDesc) +
194			fi->lengthFileIdent + le16_to_cpu(fi->lengthOfImpUse);
195
196	/* we need to figure padding, too! */
197	padlen = lengthThisIdent % UDF_NAME_PAD;
198	if (padlen)
199		lengthThisIdent += (UDF_NAME_PAD - padlen);
200	*offset = *offset + lengthThisIdent;
201
202	return fi;
203}
204
205
206short_ad *
207udf_get_fileshortad(uint8_t *ptr, int maxoffset, int *offset, int inc)
208{
209	short_ad *sa;
210
211	if ( (!ptr) || (!offset) )
212	{
213		printk(KERN_ERR "udf: udf_get_fileshortad() invalidparms\n");
214		return NULL;
215	}
216
217	if ( (*offset < 0) || ((*offset + sizeof(short_ad)) > maxoffset) )
218		return NULL;
219	else if ((sa = (short_ad *)ptr)->extLength == 0)
220		return NULL;
221
222	if (inc)
223		*offset += sizeof(short_ad);
224	return sa;
225}
226
227long_ad *
228udf_get_filelongad(uint8_t *ptr, int maxoffset, int * offset, int inc)
229{
230	long_ad *la;
231
232	if ( (!ptr) || (!offset) )
233	{
234		printk(KERN_ERR "udf: udf_get_filelongad() invalidparms\n");
235		return NULL;
236	}
237
238	if ( (*offset < 0) || ((*offset + sizeof(long_ad)) > maxoffset) )
239		return NULL;
240	else if ((la = (long_ad *)ptr)->extLength == 0)
241		return NULL;
242
243	if (inc)
244		*offset += sizeof(long_ad);
245	return la;
246}
247