ntfs_subr.c revision 71999
1/*	$NetBSD: ntfs_subr.c,v 1.23 1999/10/31 19:45:26 jdolecek Exp $	*/
2
3/*-
4 * Copyright (c) 1998, 1999 Semen Ustimenko (semenu@FreeBSD.org)
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 *
28 * $FreeBSD: head/sys/fs/ntfs/ntfs_subr.c 71999 2001-02-04 13:13:25Z phk $
29 */
30
31#include <sys/param.h>
32#include <sys/types.h>
33#include <sys/systm.h>
34#include <sys/namei.h>
35#include <sys/kernel.h>
36#include <sys/vnode.h>
37#include <sys/mount.h>
38#include <sys/bio.h>
39#include <sys/buf.h>
40#include <sys/file.h>
41#include <sys/malloc.h>
42#include <sys/lock.h>
43
44#if defined(__NetBSD__)
45#include <miscfs/specfs/specdev.h>
46#endif
47
48/* #define NTFS_DEBUG 1 */
49#include <ntfs/ntfs.h>
50#include <ntfs/ntfsmount.h>
51#include <ntfs/ntfs_inode.h>
52#include <ntfs/ntfs_vfsops.h>
53#include <ntfs/ntfs_subr.h>
54#include <ntfs/ntfs_compr.h>
55#include <ntfs/ntfs_ihash.h>
56
57#if defined(__FreeBSD__)
58MALLOC_DEFINE(M_NTFSNTVATTR, "NTFS vattr", "NTFS file attribute information");
59MALLOC_DEFINE(M_NTFSRDATA, "NTFS res data", "NTFS resident data");
60MALLOC_DEFINE(M_NTFSRUN, "NTFS vrun", "NTFS vrun storage");
61MALLOC_DEFINE(M_NTFSDECOMP, "NTFS decomp", "NTFS decompression temporary");
62#endif
63
64static int ntfs_ntlookupattr __P((struct ntfsmount *, const char *, int, int *, char **));
65static int ntfs_findvattr __P((struct ntfsmount *, struct ntnode *, struct ntvattr **, struct ntvattr **, u_int32_t, const char *, size_t, cn_t));
66static int ntfs_uastricmp __P((const wchar *, size_t, const char *, size_t));
67static int ntfs_uastrcmp __P((const wchar *, size_t, const char *, size_t));
68
69/* table for mapping Unicode chars into uppercase; it's filled upon first
70 * ntfs mount, freed upon last ntfs umount */
71static wchar *ntfs_toupper_tab;
72#define NTFS_U28(ch)		((((ch) & 0xFF) == 0) ? '_' : (ch) & 0xFF)
73#define NTFS_TOUPPER(ch)	(ntfs_toupper_tab[(unsigned char)(ch)])
74static struct lock ntfs_toupper_lock;
75static signed int ntfs_toupper_usecount;
76
77/* support macro for ntfs_ntvattrget() */
78#define NTFS_AALPCMP(aalp,type,name,namelen) (				\
79  (aalp->al_type == type) && (aalp->al_namelen == namelen) &&		\
80  !ntfs_uastrcmp(aalp->al_name,aalp->al_namelen,name,namelen) )
81
82/*
83 *
84 */
85int
86ntfs_ntvattrrele(vap)
87	struct ntvattr * vap;
88{
89	dprintf(("ntfs_ntvattrrele: ino: %d, type: 0x%x\n",
90		 vap->va_ip->i_number, vap->va_type));
91
92	ntfs_ntrele(vap->va_ip);
93
94	return (0);
95}
96
97/*
98 * find the attribute in the ntnode
99 */
100static int
101ntfs_findvattr(ntmp, ip, lvapp, vapp, type, name, namelen, vcn)
102	struct ntfsmount *ntmp;
103	struct ntnode *ip;
104	struct ntvattr **lvapp, **vapp;
105	u_int32_t type;
106	const char *name;
107	size_t namelen;
108	cn_t vcn;
109{
110	int error;
111	struct ntvattr *vap;
112
113	if((ip->i_flag & IN_LOADED) == 0) {
114		dprintf(("ntfs_findvattr: node not loaded, ino: %d\n",
115		       ip->i_number));
116		error = ntfs_loadntnode(ntmp,ip);
117		if (error) {
118			printf("ntfs_findvattr: FAILED TO LOAD INO: %d\n",
119			       ip->i_number);
120			return (error);
121		}
122	}
123
124	*lvapp = NULL;
125	*vapp = NULL;
126	LIST_FOREACH(vap, &ip->i_valist, va_list) {
127		ddprintf(("ntfs_findvattr: type: 0x%x, vcn: %d - %d\n", \
128			  vap->va_type, (u_int32_t) vap->va_vcnstart, \
129			  (u_int32_t) vap->va_vcnend));
130		if ((vap->va_type == type) &&
131		    (vap->va_vcnstart <= vcn) && (vap->va_vcnend >= vcn) &&
132		    (vap->va_namelen == namelen) &&
133		    (strncmp(name, vap->va_name, namelen) == 0)) {
134			*vapp = vap;
135			ntfs_ntref(vap->va_ip);
136			return (0);
137		}
138		if (vap->va_type == NTFS_A_ATTRLIST)
139			*lvapp = vap;
140	}
141
142	return (-1);
143}
144
145/*
146 * Search attribute specifed in ntnode (load ntnode if nessecary).
147 * If not found but ATTR_A_ATTRLIST present, read it in and search throught.
148 * VOP_VGET node needed, and lookup througth it's ntnode (load if nessesary).
149 *
150 * ntnode should be locked
151 */
152int
153ntfs_ntvattrget(
154		struct ntfsmount * ntmp,
155		struct ntnode * ip,
156		u_int32_t type,
157		const char *name,
158		cn_t vcn,
159		struct ntvattr ** vapp)
160{
161	struct ntvattr *lvap = NULL;
162	struct attr_attrlist *aalp;
163	struct attr_attrlist *nextaalp;
164	struct vnode   *newvp;
165	struct ntnode  *newip;
166	caddr_t         alpool;
167	size_t		namelen, len;
168	int             error;
169
170	*vapp = NULL;
171
172	if (name) {
173		dprintf(("ntfs_ntvattrget: " \
174			 "ino: %d, type: 0x%x, name: %s, vcn: %d\n", \
175			 ip->i_number, type, name, (u_int32_t) vcn));
176		namelen = strlen(name);
177	} else {
178		dprintf(("ntfs_ntvattrget: " \
179			 "ino: %d, type: 0x%x, vcn: %d\n", \
180			 ip->i_number, type, (u_int32_t) vcn));
181		name = "";
182		namelen = 0;
183	}
184
185	error = ntfs_findvattr(ntmp, ip, &lvap, vapp, type, name, namelen, vcn);
186	if (error >= 0)
187		return (error);
188
189	if (!lvap) {
190		dprintf(("ntfs_ntvattrget: UNEXISTED ATTRIBUTE: " \
191		       "ino: %d, type: 0x%x, name: %s, vcn: %d\n", \
192		       ip->i_number, type, name, (u_int32_t) vcn));
193		return (ENOENT);
194	}
195	/* Scan $ATTRIBUTE_LIST for requested attribute */
196	len = lvap->va_datalen;
197	MALLOC(alpool, caddr_t, len, M_TEMP, M_WAITOK);
198	error = ntfs_readntvattr_plain(ntmp, ip, lvap, 0, len, alpool, &len,
199			NULL);
200	if (error)
201		goto out;
202
203	aalp = (struct attr_attrlist *) alpool;
204	nextaalp = NULL;
205
206	for(; len > 0; aalp = nextaalp) {
207		dprintf(("ntfs_ntvattrget: " \
208			 "attrlist: ino: %d, attr: 0x%x, vcn: %d\n", \
209			 aalp->al_inumber, aalp->al_type, \
210			 (u_int32_t) aalp->al_vcnstart));
211
212		if (len > aalp->reclen) {
213			nextaalp = NTFS_NEXTREC(aalp, struct attr_attrlist *);
214		} else {
215			nextaalp = NULL;
216		}
217		len -= aalp->reclen;
218
219		if (!NTFS_AALPCMP(aalp, type, name, namelen) ||
220		    (nextaalp && (nextaalp->al_vcnstart <= vcn) &&
221		     NTFS_AALPCMP(nextaalp, type, name, namelen)))
222			continue;
223
224		dprintf(("ntfs_ntvattrget: attribute in ino: %d\n",
225				 aalp->al_inumber));
226
227		/* this is not a main record, so we can't use just plain
228		   vget() */
229		error = ntfs_vgetex(ntmp->ntm_mountp, aalp->al_inumber,
230				NTFS_A_DATA, NULL, LK_EXCLUSIVE,
231				VG_EXT, curproc, &newvp);
232		if (error) {
233			printf("ntfs_ntvattrget: CAN'T VGET INO: %d\n",
234			       aalp->al_inumber);
235			goto out;
236		}
237		newip = VTONT(newvp);
238		/* XXX have to lock ntnode */
239		error = ntfs_findvattr(ntmp, newip, &lvap, vapp,
240				type, name, namelen, vcn);
241		vput(newvp);
242		if (error == 0)
243			goto out;
244		printf("ntfs_ntvattrget: ATTRLIST ERROR.\n");
245		break;
246	}
247	error = ENOENT;
248
249	dprintf(("ntfs_ntvattrget: UNEXISTED ATTRIBUTE: " \
250	       "ino: %d, type: 0x%x, name: %.*s, vcn: %d\n", \
251	       ip->i_number, type, (int) namelen, name, (u_int32_t) vcn));
252out:
253	FREE(alpool, M_TEMP);
254	return (error);
255}
256
257/*
258 * Read ntnode from disk, make ntvattr list.
259 *
260 * ntnode should be locked
261 */
262int
263ntfs_loadntnode(
264	      struct ntfsmount * ntmp,
265	      struct ntnode * ip)
266{
267	struct filerec  *mfrp;
268	daddr_t         bn;
269	int		error,off;
270	struct attr    *ap;
271	struct ntvattr *nvap;
272
273	dprintf(("ntfs_loadntnode: loading ino: %d\n",ip->i_number));
274
275	MALLOC(mfrp, struct filerec *, ntfs_bntob(ntmp->ntm_bpmftrec),
276	       M_TEMP, M_WAITOK);
277
278	if (ip->i_number < NTFS_SYSNODESNUM) {
279		struct buf     *bp;
280
281		dprintf(("ntfs_loadntnode: read system node\n"));
282
283		bn = ntfs_cntobn(ntmp->ntm_mftcn) +
284			ntmp->ntm_bpmftrec * ip->i_number;
285
286		error = bread(ntmp->ntm_devvp,
287			      bn, ntfs_bntob(ntmp->ntm_bpmftrec),
288			      NOCRED, &bp);
289		if (error) {
290			printf("ntfs_loadntnode: BREAD FAILED\n");
291			brelse(bp);
292			goto out;
293		}
294		memcpy(mfrp, bp->b_data, ntfs_bntob(ntmp->ntm_bpmftrec));
295		bqrelse(bp);
296	} else {
297		struct vnode   *vp;
298
299		vp = ntmp->ntm_sysvn[NTFS_MFTINO];
300		error = ntfs_readattr(ntmp, VTONT(vp), NTFS_A_DATA, NULL,
301			       ip->i_number * ntfs_bntob(ntmp->ntm_bpmftrec),
302			       ntfs_bntob(ntmp->ntm_bpmftrec), mfrp, NULL);
303		if (error) {
304			printf("ntfs_loadntnode: ntfs_readattr failed\n");
305			goto out;
306		}
307	}
308
309	/* Check if magic and fixups are correct */
310	error = ntfs_procfixups(ntmp, NTFS_FILEMAGIC, (caddr_t)mfrp,
311				ntfs_bntob(ntmp->ntm_bpmftrec));
312	if (error) {
313		printf("ntfs_loadntnode: BAD MFT RECORD %d\n",
314		       (u_int32_t) ip->i_number);
315		goto out;
316	}
317
318	dprintf(("ntfs_loadntnode: load attrs for ino: %d\n",ip->i_number));
319	off = mfrp->fr_attroff;
320	ap = (struct attr *) ((caddr_t)mfrp + off);
321
322	LIST_INIT(&ip->i_valist);
323
324	while (ap->a_hdr.a_type != -1) {
325		error = ntfs_attrtontvattr(ntmp, &nvap, ap);
326		if (error)
327			break;
328		nvap->va_ip = ip;
329
330		LIST_INSERT_HEAD(&ip->i_valist, nvap, va_list);
331
332		off += ap->a_hdr.reclen;
333		ap = (struct attr *) ((caddr_t)mfrp + off);
334	}
335	if (error) {
336		printf("ntfs_loadntnode: failed to load attr ino: %d\n",
337		       ip->i_number);
338		goto out;
339	}
340
341	ip->i_mainrec = mfrp->fr_mainrec;
342	ip->i_nlink = mfrp->fr_nlink;
343	ip->i_frflag = mfrp->fr_flags;
344
345	ip->i_flag |= IN_LOADED;
346
347out:
348	FREE(mfrp, M_TEMP);
349	return (error);
350}
351
352/*
353 * Routine locks ntnode and increase usecount, just opposite of
354 * ntfs_ntput().
355 */
356int
357ntfs_ntget(ip)
358	struct ntnode *ip;
359{
360	dprintf(("ntfs_ntget: get ntnode %d: %p, usecount: %d\n",
361		ip->i_number, ip, ip->i_usecount));
362
363	mtx_enter(&ip->i_interlock, MTX_DEF);
364	ip->i_usecount++;
365	LOCKMGR(&ip->i_lock, LK_EXCLUSIVE | LK_INTERLOCK, &ip->i_interlock);
366
367	return 0;
368}
369
370/*
371 * Routine search ntnode in hash, if found: lock, inc usecount and return.
372 * If not in hash allocate structure for ntnode, prefill it, lock,
373 * inc count and return.
374 *
375 * ntnode returned locked
376 */
377int
378ntfs_ntlookup(
379	   struct ntfsmount * ntmp,
380	   ino_t ino,
381	   struct ntnode ** ipp)
382{
383	struct ntnode  *ip;
384
385	dprintf(("ntfs_ntlookup: looking for ntnode %d\n", ino));
386
387	do {
388		if ((ip = ntfs_nthashlookup(ntmp->ntm_dev, ino)) != NULL) {
389			ntfs_ntget(ip);
390			dprintf(("ntfs_ntlookup: ntnode %d: %p, usecount: %d\n",
391				ino, ip, ip->i_usecount));
392			*ipp = ip;
393			return (0);
394		}
395	} while (LOCKMGR(&ntfs_hashlock, LK_EXCLUSIVE | LK_SLEEPFAIL, NULL));
396
397	MALLOC(ip, struct ntnode *, sizeof(struct ntnode), M_NTFSNTNODE,
398		M_WAITOK | M_ZERO);
399	ddprintf(("ntfs_ntlookup: allocating ntnode: %d: %p\n", ino, ip));
400
401	/* Generic initialization */
402	ip->i_devvp = ntmp->ntm_devvp;
403	ip->i_dev = ntmp->ntm_dev;
404	ip->i_number = ino;
405	ip->i_mp = ntmp;
406
407	LIST_INIT(&ip->i_fnlist);
408
409	/* init lock and lock the newborn ntnode */
410	lockinit(&ip->i_lock, PINOD, "ntnode", 0, LK_EXCLUSIVE);
411	mtx_init(&ip->i_interlock, "ntnode interlock", MTX_DEF);
412	ntfs_ntget(ip);
413
414	ntfs_nthashins(ip);
415
416	LOCKMGR(&ntfs_hashlock, LK_RELEASE, NULL);
417
418	*ipp = ip;
419
420	dprintf(("ntfs_ntlookup: ntnode %d: %p, usecount: %d\n",
421		ino, ip, ip->i_usecount));
422
423	return (0);
424}
425
426/*
427 * Decrement usecount of ntnode and unlock it, if usecount reach zero,
428 * deallocate ntnode.
429 *
430 * ntnode should be locked on entry, and unlocked on return.
431 */
432void
433ntfs_ntput(ip)
434	struct ntnode *ip;
435{
436	struct ntvattr *vap;
437
438	dprintf(("ntfs_ntput: rele ntnode %d: %p, usecount: %d\n",
439		ip->i_number, ip, ip->i_usecount));
440
441	mtx_enter(&ip->i_interlock, MTX_DEF);
442	ip->i_usecount--;
443
444#ifdef DIAGNOSTIC
445	if (ip->i_usecount < 0) {
446		panic("ntfs_ntput: ino: %d usecount: %d \n",
447		      ip->i_number,ip->i_usecount);
448	}
449#endif
450
451	if (ip->i_usecount == 0) {
452		dprintf(("ntfs_ntput: deallocating ntnode: %d\n",
453			ip->i_number));
454
455		if (LIST_FIRST(&ip->i_fnlist))
456			panic("ntfs_ntput: ntnode has fnodes\n");
457
458		ntfs_nthashrem(ip);
459
460		while (LIST_FIRST(&ip->i_valist) != NULL) {
461			vap = LIST_FIRST(&ip->i_valist);
462			LIST_REMOVE(vap,va_list);
463			ntfs_freentvattr(vap);
464		}
465		mtx_exit(&ip->i_interlock, MTX_DEF);
466		mtx_destroy(&ip->i_interlock);
467		lockdestroy(&ip->i_lock);
468
469		FREE(ip, M_NTFSNTNODE);
470	} else {
471		LOCKMGR(&ip->i_lock, LK_RELEASE|LK_INTERLOCK, &ip->i_interlock);
472	}
473}
474
475/*
476 * increment usecount of ntnode
477 */
478void
479ntfs_ntref(ip)
480	struct ntnode *ip;
481{
482	mtx_enter(&ip->i_interlock, MTX_DEF);
483	ip->i_usecount++;
484	mtx_exit(&ip->i_interlock, MTX_DEF);
485
486	dprintf(("ntfs_ntref: ino %d, usecount: %d\n",
487		ip->i_number, ip->i_usecount));
488
489}
490
491/*
492 * Decrement usecount of ntnode.
493 */
494void
495ntfs_ntrele(ip)
496	struct ntnode *ip;
497{
498	dprintf(("ntfs_ntrele: rele ntnode %d: %p, usecount: %d\n",
499		ip->i_number, ip, ip->i_usecount));
500
501	mtx_enter(&ip->i_interlock, MTX_DEF);
502	ip->i_usecount--;
503
504	if (ip->i_usecount < 0)
505		panic("ntfs_ntrele: ino: %d usecount: %d \n",
506		      ip->i_number,ip->i_usecount);
507	mtx_exit(&ip->i_interlock, MTX_DEF);
508}
509
510/*
511 * Deallocate all memory allocated for ntvattr
512 */
513void
514ntfs_freentvattr(vap)
515	struct ntvattr * vap;
516{
517	if (vap->va_flag & NTFS_AF_INRUN) {
518		if (vap->va_vruncn)
519			FREE(vap->va_vruncn, M_NTFSRUN);
520		if (vap->va_vruncl)
521			FREE(vap->va_vruncl, M_NTFSRUN);
522	} else {
523		if (vap->va_datap)
524			FREE(vap->va_datap, M_NTFSRDATA);
525	}
526	FREE(vap, M_NTFSNTVATTR);
527}
528
529/*
530 * Convert disk image of attribute into ntvattr structure,
531 * runs are expanded also.
532 */
533int
534ntfs_attrtontvattr(
535		   struct ntfsmount * ntmp,
536		   struct ntvattr ** rvapp,
537		   struct attr * rap)
538{
539	int             error, i;
540	struct ntvattr *vap;
541
542	error = 0;
543	*rvapp = NULL;
544
545	MALLOC(vap, struct ntvattr *, sizeof(struct ntvattr),
546		M_NTFSNTVATTR, M_WAITOK | M_ZERO);
547	vap->va_ip = NULL;
548	vap->va_flag = rap->a_hdr.a_flag;
549	vap->va_type = rap->a_hdr.a_type;
550	vap->va_compression = rap->a_hdr.a_compression;
551	vap->va_index = rap->a_hdr.a_index;
552
553	ddprintf(("type: 0x%x, index: %d", vap->va_type, vap->va_index));
554
555	vap->va_namelen = rap->a_hdr.a_namelen;
556	if (rap->a_hdr.a_namelen) {
557		wchar *unp = (wchar *) ((caddr_t) rap + rap->a_hdr.a_nameoff);
558		ddprintf((", name:["));
559		for (i = 0; i < vap->va_namelen; i++) {
560			vap->va_name[i] = unp[i];
561			ddprintf(("%c", vap->va_name[i]));
562		}
563		ddprintf(("]"));
564	}
565	if (vap->va_flag & NTFS_AF_INRUN) {
566		ddprintf((", nonres."));
567		vap->va_datalen = rap->a_nr.a_datalen;
568		vap->va_allocated = rap->a_nr.a_allocated;
569		vap->va_vcnstart = rap->a_nr.a_vcnstart;
570		vap->va_vcnend = rap->a_nr.a_vcnend;
571		vap->va_compressalg = rap->a_nr.a_compressalg;
572		error = ntfs_runtovrun(&(vap->va_vruncn), &(vap->va_vruncl),
573				       &(vap->va_vruncnt),
574				       (caddr_t) rap + rap->a_nr.a_dataoff);
575	} else {
576		vap->va_compressalg = 0;
577		ddprintf((", res."));
578		vap->va_datalen = rap->a_r.a_datalen;
579		vap->va_allocated = rap->a_r.a_datalen;
580		vap->va_vcnstart = 0;
581		vap->va_vcnend = ntfs_btocn(vap->va_allocated);
582		MALLOC(vap->va_datap, caddr_t, vap->va_datalen,
583		       M_NTFSRDATA, M_WAITOK);
584		memcpy(vap->va_datap, (caddr_t) rap + rap->a_r.a_dataoff,
585		       rap->a_r.a_datalen);
586	}
587	ddprintf((", len: %d", vap->va_datalen));
588
589	if (error)
590		FREE(vap, M_NTFSNTVATTR);
591	else
592		*rvapp = vap;
593
594	ddprintf(("\n"));
595
596	return (error);
597}
598
599/*
600 * Expand run into more utilizable and more memory eating format.
601 */
602int
603ntfs_runtovrun(
604	       cn_t ** rcnp,
605	       cn_t ** rclp,
606	       u_long * rcntp,
607	       u_int8_t * run)
608{
609	u_int32_t       off;
610	u_int32_t       sz, i;
611	cn_t           *cn;
612	cn_t           *cl;
613	u_long		cnt;
614	cn_t		prev;
615	cn_t		tmp;
616
617	off = 0;
618	cnt = 0;
619	i = 0;
620	while (run[off]) {
621		off += (run[off] & 0xF) + ((run[off] >> 4) & 0xF) + 1;
622		cnt++;
623	}
624	MALLOC(cn, cn_t *, cnt * sizeof(cn_t), M_NTFSRUN, M_WAITOK);
625	MALLOC(cl, cn_t *, cnt * sizeof(cn_t), M_NTFSRUN, M_WAITOK);
626
627	off = 0;
628	cnt = 0;
629	prev = 0;
630	while (run[off]) {
631
632		sz = run[off++];
633		cl[cnt] = 0;
634
635		for (i = 0; i < (sz & 0xF); i++)
636			cl[cnt] += (u_int32_t) run[off++] << (i << 3);
637
638		sz >>= 4;
639		if (run[off + sz - 1] & 0x80) {
640			tmp = ((u_int64_t) - 1) << (sz << 3);
641			for (i = 0; i < sz; i++)
642				tmp |= (u_int64_t) run[off++] << (i << 3);
643		} else {
644			tmp = 0;
645			for (i = 0; i < sz; i++)
646				tmp |= (u_int64_t) run[off++] << (i << 3);
647		}
648		if (tmp)
649			prev = cn[cnt] = prev + tmp;
650		else
651			cn[cnt] = tmp;
652
653		cnt++;
654	}
655	*rcnp = cn;
656	*rclp = cl;
657	*rcntp = cnt;
658	return (0);
659}
660
661/*
662 * Compare unicode and ascii string case insens.
663 */
664static int
665ntfs_uastricmp(ustr, ustrlen, astr, astrlen)
666	const wchar *ustr;
667	size_t ustrlen;
668	const char *astr;
669	size_t astrlen;
670{
671	size_t             i;
672	int             res;
673
674	for (i = 0; i < ustrlen && i < astrlen; i++) {
675		res = ((int) NTFS_TOUPPER(NTFS_U28(ustr[i]))) -
676			((int)NTFS_TOUPPER(astr[i]));
677		if (res)
678			return res;
679	}
680	return (ustrlen - astrlen);
681}
682
683/*
684 * Compare unicode and ascii string case sens.
685 */
686static int
687ntfs_uastrcmp(ustr, ustrlen, astr, astrlen)
688	const wchar *ustr;
689	size_t ustrlen;
690	const char *astr;
691	size_t astrlen;
692{
693	size_t             i;
694	int             res;
695
696	for (i = 0; (i < ustrlen) && (i < astrlen); i++) {
697		res = (int) (((char)NTFS_U28(ustr[i])) - astr[i]);
698		if (res)
699			return res;
700	}
701	return (ustrlen - astrlen);
702}
703
704/*
705 * Search fnode in ntnode, if not found allocate and preinitialize.
706 *
707 * ntnode should be locked on entry.
708 */
709int
710ntfs_fget(
711	struct ntfsmount *ntmp,
712	struct ntnode *ip,
713	int attrtype,
714	char *attrname,
715	struct fnode **fpp)
716{
717	struct fnode *fp;
718
719	dprintf(("ntfs_fget: ino: %d, attrtype: 0x%x, attrname: %s\n",
720		ip->i_number,attrtype, attrname?attrname:""));
721	*fpp = NULL;
722	LIST_FOREACH(fp, &ip->i_fnlist, f_fnlist){
723		dprintf(("ntfs_fget: fnode: attrtype: %d, attrname: %s\n",
724			fp->f_attrtype, fp->f_attrname?fp->f_attrname:""));
725
726		if ((attrtype == fp->f_attrtype) &&
727		    ((!attrname && !fp->f_attrname) ||
728		     (attrname && fp->f_attrname &&
729		      !strcmp(attrname,fp->f_attrname)))){
730			dprintf(("ntfs_fget: found existed: %p\n",fp));
731			*fpp = fp;
732		}
733	}
734
735	if (*fpp)
736		return (0);
737
738	MALLOC(fp, struct fnode *, sizeof(struct fnode), M_NTFSFNODE,
739		M_WAITOK | M_ZERO);
740	dprintf(("ntfs_fget: allocating fnode: %p\n",fp));
741
742	fp->f_ip = ip;
743	fp->f_attrname = attrname;
744	if (fp->f_attrname) fp->f_flag |= FN_AATTRNAME;
745	fp->f_attrtype = attrtype;
746
747	ntfs_ntref(ip);
748
749	LIST_INSERT_HEAD(&ip->i_fnlist, fp, f_fnlist);
750
751	*fpp = fp;
752
753	return (0);
754}
755
756/*
757 * Deallocate fnode, remove it from ntnode's fnode list.
758 *
759 * ntnode should be locked.
760 */
761void
762ntfs_frele(
763	struct fnode *fp)
764{
765	struct ntnode *ip = FTONT(fp);
766
767	dprintf(("ntfs_frele: fnode: %p for %d: %p\n", fp, ip->i_number, ip));
768
769	dprintf(("ntfs_frele: deallocating fnode\n"));
770	LIST_REMOVE(fp,f_fnlist);
771	if (fp->f_flag & FN_AATTRNAME)
772		FREE(fp->f_attrname, M_TEMP);
773	if (fp->f_dirblbuf)
774		FREE(fp->f_dirblbuf, M_NTFSDIR);
775#ifdef __FreeBSD__
776	lockdestroy(&fp->f_lock);
777#endif
778	FREE(fp, M_NTFSFNODE);
779	ntfs_ntrele(ip);
780}
781
782/*
783 * Lookup attribute name in format: [[:$ATTR_TYPE]:$ATTR_NAME],
784 * $ATTR_TYPE is searched in attrdefs read from $AttrDefs.
785 * If $ATTR_TYPE nott specifed, ATTR_A_DATA assumed.
786 */
787static int
788ntfs_ntlookupattr(
789		struct ntfsmount * ntmp,
790		const char * name,
791		int namelen,
792		int *attrtype,
793		char **attrname)
794{
795	const char *sys;
796	size_t syslen, i;
797	struct ntvattrdef *adp;
798
799	if (namelen == 0)
800		return (0);
801
802	if (name[0] == '$') {
803		sys = name;
804		for (syslen = 0; syslen < namelen; syslen++) {
805			if(sys[syslen] == ':') {
806				name++;
807				namelen--;
808				break;
809			}
810		}
811		name += syslen;
812		namelen -= syslen;
813
814		adp = ntmp->ntm_ad;
815		for (i = 0; i < ntmp->ntm_adnum; i++, adp++){
816			if (syslen != adp->ad_namelen ||
817			   strncmp(sys, adp->ad_name, syslen) != 0)
818				continue;
819
820			*attrtype = adp->ad_type;
821			goto out;
822		}
823		return (ENOENT);
824	}
825
826    out:
827	if (namelen) {
828		MALLOC((*attrname), char *, namelen, M_TEMP, M_WAITOK);
829		memcpy((*attrname), name, namelen);
830		(*attrname)[namelen] = '\0';
831		*attrtype = NTFS_A_DATA;
832	}
833
834	return (0);
835}
836
837/*
838 * Lookup specifed node for filename, matching cnp,
839 * return fnode filled.
840 */
841int
842ntfs_ntlookupfile(
843	      struct ntfsmount * ntmp,
844	      struct vnode * vp,
845	      struct componentname * cnp,
846	      struct vnode ** vpp)
847{
848	struct fnode   *fp = VTOF(vp);
849	struct ntnode  *ip = FTONT(fp);
850	struct ntvattr *vap;	/* Root attribute */
851	cn_t            cn;	/* VCN in current attribute */
852	caddr_t         rdbuf;	/* Buffer to read directory's blocks  */
853	u_int32_t       blsize;
854	u_int32_t       rdsize;	/* Length of data to read from current block */
855	struct attr_indexentry *iep;
856	int             error, res, anamelen, fnamelen;
857	const char     *fname,*aname;
858	u_int32_t       aoff;
859	int attrtype = NTFS_A_DATA;
860	char *attrname = NULL;
861	struct fnode   *nfp;
862	struct vnode   *nvp;
863	enum vtype	f_type;
864
865	error = ntfs_ntget(ip);
866	if (error)
867		return (error);
868
869	error = ntfs_ntvattrget(ntmp, ip, NTFS_A_INDXROOT, "$I30", 0, &vap);
870	if (error || (vap->va_flag & NTFS_AF_INRUN))
871		return (ENOTDIR);
872
873	blsize = vap->va_a_iroot->ir_size;
874	rdsize = vap->va_datalen;
875
876	/*
877	 * Divide file name into: foofilefoofilefoofile[:attrspec]
878	 * Store like this:       fname:fnamelen       [aname:anamelen]
879	 */
880	fname = cnp->cn_nameptr;
881	aname = NULL;
882	anamelen = 0;
883	for (fnamelen = 0; fnamelen < cnp->cn_namelen; fnamelen++)
884		if(fname[fnamelen] == ':') {
885			aname = fname + fnamelen + 1;
886			anamelen = cnp->cn_namelen - fnamelen - 1;
887			dprintf(("ntfs_ntlookupfile: %s (%d), attr: %s (%d)\n",
888				fname, fnamelen, aname, anamelen));
889			break;
890		}
891
892	dprintf(("ntfs_ntlookupfile: blksz: %d, rdsz: %d\n", blsize, rdsize));
893
894	MALLOC(rdbuf, caddr_t, blsize, M_TEMP, M_WAITOK);
895
896	error = ntfs_readattr(ntmp, ip, NTFS_A_INDXROOT, "$I30",
897			       0, rdsize, rdbuf, NULL);
898	if (error)
899		goto fail;
900
901	aoff = sizeof(struct attr_indexroot);
902
903	do {
904		iep = (struct attr_indexentry *) (rdbuf + aoff);
905
906		for (; !(iep->ie_flag & NTFS_IEFLAG_LAST) && (rdsize > aoff);
907			aoff += iep->reclen,
908			iep = (struct attr_indexentry *) (rdbuf + aoff))
909		{
910			ddprintf(("scan: %d, %d\n",
911				  (u_int32_t) iep->ie_number,
912				  (u_int32_t) iep->ie_fnametype));
913
914			/* check the name - the case-insensitible check
915			 * has to come first, to break from this for loop
916			 * if needed, so we can dive correctly */
917			res = ntfs_uastricmp(iep->ie_fname, iep->ie_fnamelen,
918				fname, fnamelen);
919			if (res > 0) break;
920			if (res < 0) continue;
921
922			if (iep->ie_fnametype == 0 ||
923			    !(ntmp->ntm_flag & NTFS_MFLAG_CASEINS))
924			{
925				res = ntfs_uastrcmp(iep->ie_fname,
926					iep->ie_fnamelen, fname, fnamelen);
927				if (res != 0) continue;
928			}
929
930			if (aname) {
931				error = ntfs_ntlookupattr(ntmp,
932					aname, anamelen,
933					&attrtype, &attrname);
934				if (error)
935					goto fail;
936			}
937
938			/* Check if we've found ourself */
939			if ((iep->ie_number == ip->i_number) &&
940			    (attrtype == fp->f_attrtype) &&
941			    ((!attrname && !fp->f_attrname) ||
942			     (attrname && fp->f_attrname &&
943			      !strcmp(attrname, fp->f_attrname))))
944			{
945				VREF(vp);
946				*vpp = vp;
947				error = 0;
948				goto fail;
949			}
950
951			/* free the buffer returned by ntfs_ntlookupattr() */
952			if (attrname) {
953				FREE(attrname, M_TEMP);
954				attrname = NULL;
955			}
956
957			/* vget node, but don't load it */
958			error = ntfs_vgetex(ntmp->ntm_mountp,
959				   iep->ie_number, attrtype, attrname,
960				   LK_EXCLUSIVE, VG_DONTLOADIN | VG_DONTVALIDFN,
961				   curproc, &nvp);
962			if (error)
963				goto fail;
964
965			nfp = VTOF(nvp);
966
967			if (nfp->f_flag & FN_VALID) {
968				*vpp = nvp;
969				goto fail;
970			}
971
972			nfp->f_fflag = iep->ie_fflag;
973			nfp->f_pnumber = iep->ie_fpnumber;
974			nfp->f_times = iep->ie_ftimes;
975
976			if((nfp->f_fflag & NTFS_FFLAG_DIR) &&
977			   (nfp->f_attrtype == NTFS_A_DATA) &&
978			   (nfp->f_attrname == NULL))
979				f_type = VDIR;
980			else
981				f_type = VREG;
982
983			nvp->v_type = f_type;
984
985			if ((nfp->f_attrtype == NTFS_A_DATA) &&
986			    (nfp->f_attrname == NULL))
987			{
988				/* Opening default attribute */
989				nfp->f_size = iep->ie_fsize;
990				nfp->f_allocated = iep->ie_fallocated;
991				nfp->f_flag |= FN_PRELOADED;
992			} else {
993				error = ntfs_filesize(ntmp, nfp,
994					    &nfp->f_size, &nfp->f_allocated);
995				if (error) {
996					vput(nvp);
997					goto fail;
998				}
999			}
1000
1001			nfp->f_flag &= ~FN_VALID;
1002			*vpp = nvp;
1003			goto fail;
1004		}
1005
1006		/* Dive if possible */
1007		if (iep->ie_flag & NTFS_IEFLAG_SUBNODE) {
1008			dprintf(("ntfs_ntlookupfile: diving\n"));
1009
1010			cn = *(cn_t *) (rdbuf + aoff +
1011					iep->reclen - sizeof(cn_t));
1012			rdsize = blsize;
1013
1014			error = ntfs_readattr(ntmp, ip, NTFS_A_INDX, "$I30",
1015					ntfs_cntob(cn), rdsize, rdbuf, NULL);
1016			if (error)
1017				goto fail;
1018
1019			error = ntfs_procfixups(ntmp, NTFS_INDXMAGIC,
1020						rdbuf, rdsize);
1021			if (error)
1022				goto fail;
1023
1024			aoff = (((struct attr_indexalloc *) rdbuf)->ia_hdrsize +
1025				0x18);
1026		} else {
1027			dprintf(("ntfs_ntlookupfile: nowhere to dive :-(\n"));
1028			error = ENOENT;
1029			break;
1030		}
1031	} while (1);
1032
1033	dprintf(("finish\n"));
1034
1035fail:
1036	if (attrname) FREE(attrname, M_TEMP);
1037	ntfs_ntvattrrele(vap);
1038	ntfs_ntput(ip);
1039	FREE(rdbuf, M_TEMP);
1040	return (error);
1041}
1042
1043/*
1044 * Check if name type is permitted to show.
1045 */
1046int
1047ntfs_isnamepermitted(
1048		     struct ntfsmount * ntmp,
1049		     struct attr_indexentry * iep)
1050{
1051	if (ntmp->ntm_flag & NTFS_MFLAG_ALLNAMES)
1052		return 1;
1053
1054	switch (iep->ie_fnametype) {
1055	case 2:
1056		ddprintf(("ntfs_isnamepermitted: skiped DOS name\n"));
1057		return 0;
1058	case 0: case 1: case 3:
1059		return 1;
1060	default:
1061		printf("ntfs_isnamepermitted: " \
1062		       "WARNING! Unknown file name type: %d\n",
1063		       iep->ie_fnametype);
1064		break;
1065	}
1066	return 0;
1067}
1068
1069/*
1070 * Read ntfs dir like stream of attr_indexentry, not like btree of them.
1071 * This is done by scaning $BITMAP:$I30 for busy clusters and reading them.
1072 * Ofcouse $INDEX_ROOT:$I30 is read before. Last read values are stored in
1073 * fnode, so we can skip toward record number num almost immediatly.
1074 * Anyway this is rather slow routine. The problem is that we don't know
1075 * how many records are there in $INDEX_ALLOCATION:$I30 block.
1076 */
1077int
1078ntfs_ntreaddir(
1079	       struct ntfsmount * ntmp,
1080	       struct fnode * fp,
1081	       u_int32_t num,
1082	       struct attr_indexentry ** riepp)
1083{
1084	struct ntnode  *ip = FTONT(fp);
1085	struct ntvattr *vap = NULL;	/* IndexRoot attribute */
1086	struct ntvattr *bmvap = NULL;	/* BitMap attribute */
1087	struct ntvattr *iavap = NULL;	/* IndexAllocation attribute */
1088	caddr_t         rdbuf;		/* Buffer to read directory's blocks  */
1089	u_char         *bmp = NULL;	/* Bitmap */
1090	u_int32_t       blsize;		/* Index allocation size (2048) */
1091	u_int32_t       rdsize;		/* Length of data to read */
1092	u_int32_t       attrnum;	/* Current attribute type */
1093	u_int32_t       cpbl = 1;	/* Clusters per directory block */
1094	u_int32_t       blnum;
1095	struct attr_indexentry *iep;
1096	int             error = ENOENT;
1097	u_int32_t       aoff, cnum;
1098
1099	dprintf(("ntfs_ntreaddir: read ino: %d, num: %d\n", ip->i_number, num));
1100	error = ntfs_ntget(ip);
1101	if (error)
1102		return (error);
1103
1104	error = ntfs_ntvattrget(ntmp, ip, NTFS_A_INDXROOT, "$I30", 0, &vap);
1105	if (error)
1106		return (ENOTDIR);
1107
1108	if (fp->f_dirblbuf == NULL) {
1109		fp->f_dirblsz = vap->va_a_iroot->ir_size;
1110		MALLOC(fp->f_dirblbuf, caddr_t,
1111		       max(vap->va_datalen,fp->f_dirblsz), M_NTFSDIR, M_WAITOK);
1112	}
1113
1114	blsize = fp->f_dirblsz;
1115	rdbuf = fp->f_dirblbuf;
1116
1117	dprintf(("ntfs_ntreaddir: rdbuf: 0x%p, blsize: %d\n", rdbuf, blsize));
1118
1119	if (vap->va_a_iroot->ir_flag & NTFS_IRFLAG_INDXALLOC) {
1120		error = ntfs_ntvattrget(ntmp, ip, NTFS_A_INDXBITMAP, "$I30",
1121					0, &bmvap);
1122		if (error) {
1123			error = ENOTDIR;
1124			goto fail;
1125		}
1126		MALLOC(bmp, u_char *, bmvap->va_datalen, M_TEMP, M_WAITOK);
1127		error = ntfs_readattr(ntmp, ip, NTFS_A_INDXBITMAP, "$I30", 0,
1128				       bmvap->va_datalen, bmp, NULL);
1129		if (error)
1130			goto fail;
1131
1132		error = ntfs_ntvattrget(ntmp, ip, NTFS_A_INDX, "$I30",
1133					0, &iavap);
1134		if (error) {
1135			error = ENOTDIR;
1136			goto fail;
1137		}
1138		cpbl = ntfs_btocn(blsize + ntfs_cntob(1) - 1);
1139		dprintf(("ntfs_ntreaddir: indexalloc: %d, cpbl: %d\n",
1140			 iavap->va_datalen, cpbl));
1141	} else {
1142		dprintf(("ntfs_ntreadidir: w/o BitMap and IndexAllocation\n"));
1143		iavap = bmvap = NULL;
1144		bmp = NULL;
1145	}
1146
1147	/* Try use previous values */
1148	if ((fp->f_lastdnum < num) && (fp->f_lastdnum != 0)) {
1149		attrnum = fp->f_lastdattr;
1150		aoff = fp->f_lastdoff;
1151		blnum = fp->f_lastdblnum;
1152		cnum = fp->f_lastdnum;
1153	} else {
1154		attrnum = NTFS_A_INDXROOT;
1155		aoff = sizeof(struct attr_indexroot);
1156		blnum = 0;
1157		cnum = 0;
1158	}
1159
1160	do {
1161		dprintf(("ntfs_ntreaddir: scan: 0x%x, %d, %d, %d, %d\n",
1162			 attrnum, (u_int32_t) blnum, cnum, num, aoff));
1163		rdsize = (attrnum == NTFS_A_INDXROOT) ? vap->va_datalen : blsize;
1164		error = ntfs_readattr(ntmp, ip, attrnum, "$I30",
1165				ntfs_cntob(blnum * cpbl), rdsize, rdbuf, NULL);
1166		if (error)
1167			goto fail;
1168
1169		if (attrnum == NTFS_A_INDX) {
1170			error = ntfs_procfixups(ntmp, NTFS_INDXMAGIC,
1171						rdbuf, rdsize);
1172			if (error)
1173				goto fail;
1174		}
1175		if (aoff == 0)
1176			aoff = (attrnum == NTFS_A_INDX) ?
1177				(0x18 + ((struct attr_indexalloc *) rdbuf)->ia_hdrsize) :
1178				sizeof(struct attr_indexroot);
1179
1180		iep = (struct attr_indexentry *) (rdbuf + aoff);
1181		for (; !(iep->ie_flag & NTFS_IEFLAG_LAST) && (rdsize > aoff);
1182			aoff += iep->reclen,
1183			iep = (struct attr_indexentry *) (rdbuf + aoff))
1184		{
1185			if (!ntfs_isnamepermitted(ntmp, iep)) continue;
1186
1187			if (cnum >= num) {
1188				fp->f_lastdnum = cnum;
1189				fp->f_lastdoff = aoff;
1190				fp->f_lastdblnum = blnum;
1191				fp->f_lastdattr = attrnum;
1192
1193				*riepp = iep;
1194
1195				error = 0;
1196				goto fail;
1197			}
1198			cnum++;
1199		}
1200
1201		if (iavap) {
1202			if (attrnum == NTFS_A_INDXROOT)
1203				blnum = 0;
1204			else
1205				blnum++;
1206
1207			while (ntfs_cntob(blnum * cpbl) < iavap->va_datalen) {
1208				if (bmp[blnum >> 3] & (1 << (blnum & 3)))
1209					break;
1210				blnum++;
1211			}
1212
1213			attrnum = NTFS_A_INDX;
1214			aoff = 0;
1215			if (ntfs_cntob(blnum * cpbl) >= iavap->va_datalen)
1216				break;
1217			dprintf(("ntfs_ntreaddir: blnum: %d\n", (u_int32_t) blnum));
1218		}
1219	} while (iavap);
1220
1221	*riepp = NULL;
1222	fp->f_lastdnum = 0;
1223
1224fail:
1225	if (vap)
1226		ntfs_ntvattrrele(vap);
1227	if (bmvap)
1228		ntfs_ntvattrrele(bmvap);
1229	if (iavap)
1230		ntfs_ntvattrrele(iavap);
1231	if (bmp)
1232		FREE(bmp, M_TEMP);
1233	ntfs_ntput(ip);
1234	return (error);
1235}
1236
1237/*
1238 * Convert NTFS times that are in 100 ns units and begins from
1239 * 1601 Jan 1 into unix times.
1240 */
1241struct timespec
1242ntfs_nttimetounix(
1243		  u_int64_t nt)
1244{
1245	struct timespec t;
1246
1247	/* WindowNT times are in 100 ns and from 1601 Jan 1 */
1248	t.tv_nsec = (nt % (1000 * 1000 * 10)) * 100;
1249	t.tv_sec = nt / (1000 * 1000 * 10) -
1250		369LL * 365LL * 24LL * 60LL * 60LL -
1251		89LL * 1LL * 24LL * 60LL * 60LL;
1252	return (t);
1253}
1254
1255/*
1256 * Get file times from NTFS_A_NAME attribute.
1257 */
1258int
1259ntfs_times(
1260	   struct ntfsmount * ntmp,
1261	   struct ntnode * ip,
1262	   ntfs_times_t * tm)
1263{
1264	struct ntvattr *vap;
1265	int             error;
1266
1267	dprintf(("ntfs_times: ino: %d...\n", ip->i_number));
1268
1269	error = ntfs_ntget(ip);
1270	if (error)
1271		return (error);
1272
1273	error = ntfs_ntvattrget(ntmp, ip, NTFS_A_NAME, NULL, 0, &vap);
1274	if (error) {
1275		ntfs_ntput(ip);
1276		return (error);
1277	}
1278	*tm = vap->va_a_name->n_times;
1279	ntfs_ntvattrrele(vap);
1280	ntfs_ntput(ip);
1281
1282	return (0);
1283}
1284
1285/*
1286 * Get file sizes from corresponding attribute.
1287 *
1288 * ntnode under fnode should be locked.
1289 */
1290int
1291ntfs_filesize(
1292	      struct ntfsmount * ntmp,
1293	      struct fnode * fp,
1294	      u_int64_t * size,
1295	      u_int64_t * bytes)
1296{
1297	struct ntvattr *vap;
1298	struct ntnode *ip = FTONT(fp);
1299	u_int64_t       sz, bn;
1300	int             error;
1301
1302	dprintf(("ntfs_filesize: ino: %d\n", ip->i_number));
1303
1304	error = ntfs_ntvattrget(ntmp, ip,
1305		fp->f_attrtype, fp->f_attrname, 0, &vap);
1306	if (error)
1307		return (error);
1308
1309	bn = vap->va_allocated;
1310	sz = vap->va_datalen;
1311
1312	dprintf(("ntfs_filesize: %d bytes (%d bytes allocated)\n",
1313		(u_int32_t) sz, (u_int32_t) bn));
1314
1315	if (size)
1316		*size = sz;
1317	if (bytes)
1318		*bytes = bn;
1319
1320	ntfs_ntvattrrele(vap);
1321
1322	return (0);
1323}
1324
1325/*
1326 * This is one of write routine.
1327 */
1328int
1329ntfs_writeattr_plain(
1330	struct ntfsmount * ntmp,
1331	struct ntnode * ip,
1332	u_int32_t attrnum,
1333	char *attrname,
1334	off_t roff,
1335	size_t rsize,
1336	void *rdata,
1337	size_t * initp,
1338	struct uio *uio)
1339{
1340	size_t          init;
1341	int             error = 0;
1342	off_t           off = roff, left = rsize, towrite;
1343	caddr_t         data = rdata;
1344	struct ntvattr *vap;
1345	*initp = 0;
1346
1347	while (left) {
1348		error = ntfs_ntvattrget(ntmp, ip, attrnum, attrname,
1349					ntfs_btocn(off), &vap);
1350		if (error)
1351			return (error);
1352		towrite = min(left, ntfs_cntob(vap->va_vcnend + 1) - off);
1353		ddprintf(("ntfs_writeattr_plain: o: %d, s: %d (%d - %d)\n",
1354			 (u_int32_t) off, (u_int32_t) towrite,
1355			 (u_int32_t) vap->va_vcnstart,
1356			 (u_int32_t) vap->va_vcnend));
1357		error = ntfs_writentvattr_plain(ntmp, ip, vap,
1358					 off - ntfs_cntob(vap->va_vcnstart),
1359					 towrite, data, &init, uio);
1360		if (error) {
1361			printf("ntfs_writeattr_plain: " \
1362			       "ntfs_writentvattr_plain failed: o: %d, s: %d\n",
1363			       (u_int32_t) off, (u_int32_t) towrite);
1364			printf("ntfs_writeattr_plain: attrib: %d - %d\n",
1365			       (u_int32_t) vap->va_vcnstart,
1366			       (u_int32_t) vap->va_vcnend);
1367			ntfs_ntvattrrele(vap);
1368			break;
1369		}
1370		ntfs_ntvattrrele(vap);
1371		left -= towrite;
1372		off += towrite;
1373		data = data + towrite;
1374		*initp += init;
1375	}
1376
1377	return (error);
1378}
1379
1380/*
1381 * This is one of write routine.
1382 *
1383 * ntnode should be locked.
1384 */
1385int
1386ntfs_writentvattr_plain(
1387	struct ntfsmount * ntmp,
1388	struct ntnode * ip,
1389	struct ntvattr * vap,
1390	off_t roff,
1391	size_t rsize,
1392	void *rdata,
1393	size_t * initp,
1394	struct uio *uio)
1395{
1396	int             error = 0;
1397	int             off;
1398	int             cnt;
1399	cn_t            ccn, ccl, cn, left, cl;
1400	caddr_t         data = rdata;
1401	struct buf     *bp;
1402	size_t          tocopy;
1403
1404	*initp = 0;
1405
1406	if ((vap->va_flag & NTFS_AF_INRUN) == 0) {
1407		printf("ntfs_writevattr_plain: CAN'T WRITE RES. ATTRIBUTE\n");
1408		return ENOTTY;
1409	}
1410
1411	ddprintf(("ntfs_writentvattr_plain: data in run: %d chains\n",
1412		 vap->va_vruncnt));
1413
1414	off = roff;
1415	left = rsize;
1416	ccl = 0;
1417	ccn = 0;
1418	cnt = 0;
1419	for (; left && (cnt < vap->va_vruncnt); cnt++) {
1420		ccn = vap->va_vruncn[cnt];
1421		ccl = vap->va_vruncl[cnt];
1422
1423		ddprintf(("ntfs_writentvattr_plain: " \
1424			 "left %d, cn: 0x%x, cl: %d, off: %d\n", \
1425			 (u_int32_t) left, (u_int32_t) ccn, \
1426			 (u_int32_t) ccl, (u_int32_t) off));
1427
1428		if (ntfs_cntob(ccl) < off) {
1429			off -= ntfs_cntob(ccl);
1430			cnt++;
1431			continue;
1432		}
1433		if (!ccn && ip->i_number != NTFS_BOOTINO)
1434			continue; /* XXX */
1435
1436		ccl -= ntfs_btocn(off);
1437		cn = ccn + ntfs_btocn(off);
1438		off = ntfs_btocnoff(off);
1439
1440		while (left && ccl) {
1441#if defined(__FreeBSD__)
1442			tocopy = min(left,
1443				  min(ntfs_cntob(ccl) - off, MAXBSIZE - off));
1444#else
1445			/* under NetBSD, bread() can read
1446			 * maximum one block worth of data */
1447			tocopy = min(left, ntmp->ntm_bps - off);
1448#endif
1449			cl = ntfs_btocl(tocopy + off);
1450			ddprintf(("ntfs_writentvattr_plain: write: " \
1451				"cn: 0x%x cl: %d, off: %d len: %d, left: %d\n",
1452				(u_int32_t) cn, (u_int32_t) cl,
1453				(u_int32_t) off, (u_int32_t) tocopy,
1454				(u_int32_t) left));
1455			if ((off == 0) && (tocopy == ntfs_cntob(cl)))
1456			{
1457				bp = getblk(ntmp->ntm_devvp, ntfs_cntobn(cn),
1458					    ntfs_cntob(cl), 0, 0);
1459				clrbuf(bp);
1460			} else {
1461				error = bread(ntmp->ntm_devvp, ntfs_cntobn(cn),
1462					      ntfs_cntob(cl), NOCRED, &bp);
1463				if (error) {
1464					brelse(bp);
1465					return (error);
1466				}
1467			}
1468			if (uio)
1469				uiomove(bp->b_data + off, tocopy, uio);
1470			else
1471				memcpy(bp->b_data + off, data, tocopy);
1472			bawrite(bp);
1473			data = data + tocopy;
1474			*initp += tocopy;
1475			off = 0;
1476			left -= tocopy;
1477			cn += cl;
1478			ccl -= cl;
1479		}
1480	}
1481
1482	if (left) {
1483		printf("ntfs_writentvattr_plain: POSSIBLE RUN ERROR\n");
1484		error = EINVAL;
1485	}
1486
1487	return (error);
1488}
1489
1490/*
1491 * This is one of read routines.
1492 *
1493 * ntnode should be locked.
1494 */
1495int
1496ntfs_readntvattr_plain(
1497	struct ntfsmount * ntmp,
1498	struct ntnode * ip,
1499	struct ntvattr * vap,
1500	off_t roff,
1501	size_t rsize,
1502	void *rdata,
1503	size_t * initp,
1504	struct uio *uio)
1505{
1506	int             error = 0;
1507	int             off;
1508
1509	*initp = 0;
1510	if (vap->va_flag & NTFS_AF_INRUN) {
1511		int             cnt;
1512		cn_t            ccn, ccl, cn, left, cl;
1513		caddr_t         data = rdata;
1514		struct buf     *bp;
1515		size_t          tocopy;
1516
1517		ddprintf(("ntfs_readntvattr_plain: data in run: %d chains\n",
1518			 vap->va_vruncnt));
1519
1520		off = roff;
1521		left = rsize;
1522		ccl = 0;
1523		ccn = 0;
1524		cnt = 0;
1525		while (left && (cnt < vap->va_vruncnt)) {
1526			ccn = vap->va_vruncn[cnt];
1527			ccl = vap->va_vruncl[cnt];
1528
1529			ddprintf(("ntfs_readntvattr_plain: " \
1530				 "left %d, cn: 0x%x, cl: %d, off: %d\n", \
1531				 (u_int32_t) left, (u_int32_t) ccn, \
1532				 (u_int32_t) ccl, (u_int32_t) off));
1533
1534			if (ntfs_cntob(ccl) < off) {
1535				off -= ntfs_cntob(ccl);
1536				cnt++;
1537				continue;
1538			}
1539			if (ccn || ip->i_number == NTFS_BOOTINO) {
1540				ccl -= ntfs_btocn(off);
1541				cn = ccn + ntfs_btocn(off);
1542				off = ntfs_btocnoff(off);
1543
1544				while (left && ccl) {
1545#if defined(__FreeBSD__)
1546					tocopy = min(left,
1547						  min(ntfs_cntob(ccl) - off,
1548						      MAXBSIZE - off));
1549#else
1550					/* under NetBSD, bread() can read
1551					 * maximum one block worth of data */
1552					tocopy = min(left,
1553						ntmp->ntm_bps - off);
1554#endif
1555					cl = ntfs_btocl(tocopy + off);
1556					ddprintf(("ntfs_readntvattr_plain: " \
1557						"read: cn: 0x%x cl: %d, " \
1558						"off: %d len: %d, left: %d\n",
1559						(u_int32_t) cn,
1560						(u_int32_t) cl,
1561						(u_int32_t) off,
1562						(u_int32_t) tocopy,
1563						(u_int32_t) left));
1564					error = bread(ntmp->ntm_devvp,
1565						      ntfs_cntobn(cn),
1566						      ntfs_cntob(cl),
1567						      NOCRED, &bp);
1568					if (error) {
1569						brelse(bp);
1570						return (error);
1571					}
1572					if (uio) {
1573						uiomove(bp->b_data + off,
1574							tocopy, uio);
1575					} else {
1576						memcpy(data, bp->b_data + off,
1577							tocopy);
1578					}
1579					brelse(bp);
1580					data = data + tocopy;
1581					*initp += tocopy;
1582					off = 0;
1583					left -= tocopy;
1584					cn += cl;
1585					ccl -= cl;
1586				}
1587			} else {
1588				tocopy = min(left, ntfs_cntob(ccl) - off);
1589				ddprintf(("ntfs_readntvattr_plain: "
1590					"hole: ccn: 0x%x ccl: %d, off: %d, " \
1591					" len: %d, left: %d\n",
1592					(u_int32_t) ccn, (u_int32_t) ccl,
1593					(u_int32_t) off, (u_int32_t) tocopy,
1594					(u_int32_t) left));
1595				left -= tocopy;
1596				off = 0;
1597				if (uio) {
1598					size_t remains = tocopy;
1599					for(; remains; remains++)
1600						uiomove("", 1, uio);
1601				} else
1602					bzero(data, tocopy);
1603				data = data + tocopy;
1604			}
1605			cnt++;
1606		}
1607		if (left) {
1608			printf("ntfs_readntvattr_plain: POSSIBLE RUN ERROR\n");
1609			error = E2BIG;
1610		}
1611	} else {
1612		ddprintf(("ntfs_readnvattr_plain: data is in mft record\n"));
1613		if (uio)
1614			uiomove(vap->va_datap + roff, rsize, uio);
1615		else
1616			memcpy(rdata, vap->va_datap + roff, rsize);
1617		*initp += rsize;
1618	}
1619
1620	return (error);
1621}
1622
1623/*
1624 * This is one of read routines.
1625 */
1626int
1627ntfs_readattr_plain(
1628	struct ntfsmount * ntmp,
1629	struct ntnode * ip,
1630	u_int32_t attrnum,
1631	char *attrname,
1632	off_t roff,
1633	size_t rsize,
1634	void *rdata,
1635	size_t * initp,
1636	struct uio *uio)
1637{
1638	size_t          init;
1639	int             error = 0;
1640	off_t           off = roff, left = rsize, toread;
1641	caddr_t         data = rdata;
1642	struct ntvattr *vap;
1643	*initp = 0;
1644
1645	while (left) {
1646		error = ntfs_ntvattrget(ntmp, ip, attrnum, attrname,
1647					ntfs_btocn(off), &vap);
1648		if (error)
1649			return (error);
1650		toread = min(left, ntfs_cntob(vap->va_vcnend + 1) - off);
1651		ddprintf(("ntfs_readattr_plain: o: %d, s: %d (%d - %d)\n",
1652			 (u_int32_t) off, (u_int32_t) toread,
1653			 (u_int32_t) vap->va_vcnstart,
1654			 (u_int32_t) vap->va_vcnend));
1655		error = ntfs_readntvattr_plain(ntmp, ip, vap,
1656					 off - ntfs_cntob(vap->va_vcnstart),
1657					 toread, data, &init, uio);
1658		if (error) {
1659			printf("ntfs_readattr_plain: " \
1660			       "ntfs_readntvattr_plain failed: o: %d, s: %d\n",
1661			       (u_int32_t) off, (u_int32_t) toread);
1662			printf("ntfs_readattr_plain: attrib: %d - %d\n",
1663			       (u_int32_t) vap->va_vcnstart,
1664			       (u_int32_t) vap->va_vcnend);
1665			ntfs_ntvattrrele(vap);
1666			break;
1667		}
1668		ntfs_ntvattrrele(vap);
1669		left -= toread;
1670		off += toread;
1671		data = data + toread;
1672		*initp += init;
1673	}
1674
1675	return (error);
1676}
1677
1678/*
1679 * This is one of read routines.
1680 */
1681int
1682ntfs_readattr(
1683	struct ntfsmount * ntmp,
1684	struct ntnode * ip,
1685	u_int32_t attrnum,
1686	char *attrname,
1687	off_t roff,
1688	size_t rsize,
1689	void *rdata,
1690	struct uio *uio)
1691{
1692	int             error = 0;
1693	struct ntvattr *vap;
1694	size_t          init;
1695
1696	ddprintf(("ntfs_readattr: reading %d: 0x%x, from %d size %d bytes\n",
1697	       ip->i_number, attrnum, (u_int32_t) roff, (u_int32_t) rsize));
1698
1699	error = ntfs_ntvattrget(ntmp, ip, attrnum, attrname, 0, &vap);
1700	if (error)
1701		return (error);
1702
1703	if ((roff > vap->va_datalen) ||
1704	    (roff + rsize > vap->va_datalen)) {
1705		ddprintf(("ntfs_readattr: offset too big\n"));
1706		ntfs_ntvattrrele(vap);
1707		return (E2BIG);
1708	}
1709	if (vap->va_compression && vap->va_compressalg) {
1710		u_int8_t       *cup;
1711		u_int8_t       *uup;
1712		off_t           off = roff, left = rsize, tocopy;
1713		caddr_t         data = rdata;
1714		cn_t            cn;
1715
1716		ddprintf(("ntfs_ntreadattr: compression: %d\n",
1717			 vap->va_compressalg));
1718
1719		MALLOC(cup, u_int8_t *, ntfs_cntob(NTFS_COMPUNIT_CL),
1720		       M_NTFSDECOMP, M_WAITOK);
1721		MALLOC(uup, u_int8_t *, ntfs_cntob(NTFS_COMPUNIT_CL),
1722		       M_NTFSDECOMP, M_WAITOK);
1723
1724		cn = (ntfs_btocn(roff)) & (~(NTFS_COMPUNIT_CL - 1));
1725		off = roff - ntfs_cntob(cn);
1726
1727		while (left) {
1728			error = ntfs_readattr_plain(ntmp, ip, attrnum,
1729						  attrname, ntfs_cntob(cn),
1730					          ntfs_cntob(NTFS_COMPUNIT_CL),
1731						  cup, &init, NULL);
1732			if (error)
1733				break;
1734
1735			tocopy = min(left, ntfs_cntob(NTFS_COMPUNIT_CL) - off);
1736
1737			if (init == ntfs_cntob(NTFS_COMPUNIT_CL)) {
1738				if (uio)
1739					uiomove(cup + off, tocopy, uio);
1740				else
1741					memcpy(data, cup + off, tocopy);
1742			} else if (init == 0) {
1743				if (uio) {
1744					size_t remains = tocopy;
1745					for(; remains; remains--)
1746						uiomove("", 1, uio);
1747				}
1748				else
1749					bzero(data, tocopy);
1750			} else {
1751				error = ntfs_uncompunit(ntmp, uup, cup);
1752				if (error)
1753					break;
1754				if (uio)
1755					uiomove(uup + off, tocopy, uio);
1756				else
1757					memcpy(data, uup + off, tocopy);
1758			}
1759
1760			left -= tocopy;
1761			data = data + tocopy;
1762			off += tocopy - ntfs_cntob(NTFS_COMPUNIT_CL);
1763			cn += NTFS_COMPUNIT_CL;
1764		}
1765
1766		FREE(uup, M_NTFSDECOMP);
1767		FREE(cup, M_NTFSDECOMP);
1768	} else
1769		error = ntfs_readattr_plain(ntmp, ip, attrnum, attrname,
1770					     roff, rsize, rdata, &init, uio);
1771	ntfs_ntvattrrele(vap);
1772	return (error);
1773}
1774
1775#if UNUSED_CODE
1776int
1777ntfs_parserun(
1778	      cn_t * cn,
1779	      cn_t * cl,
1780	      u_int8_t * run,
1781	      u_long len,
1782	      u_long *off)
1783{
1784	u_int8_t        sz;
1785	int             i;
1786
1787	if (NULL == run) {
1788		printf("ntfs_parsetun: run == NULL\n");
1789		return (EINVAL);
1790	}
1791	sz = run[(*off)++];
1792	if (0 == sz) {
1793		printf("ntfs_parserun: trying to go out of run\n");
1794		return (E2BIG);
1795	}
1796	*cl = 0;
1797	if ((sz & 0xF) > 8 || (*off) + (sz & 0xF) > len) {
1798		printf("ntfs_parserun: " \
1799		       "bad run: length too big: sz: 0x%02x (%ld < %ld + sz)\n",
1800		       sz, len, *off);
1801		return (EINVAL);
1802	}
1803	for (i = 0; i < (sz & 0xF); i++)
1804		*cl += (u_int32_t) run[(*off)++] << (i << 3);
1805
1806	sz >>= 4;
1807	if ((sz & 0xF) > 8 || (*off) + (sz & 0xF) > len) {
1808		printf("ntfs_parserun: " \
1809		       "bad run: length too big: sz: 0x%02x (%ld < %ld + sz)\n",
1810		       sz, len, *off);
1811		return (EINVAL);
1812	}
1813	for (i = 0; i < (sz & 0xF); i++)
1814		*cn += (u_int32_t) run[(*off)++] << (i << 3);
1815
1816	return (0);
1817}
1818#endif
1819
1820/*
1821 * Process fixup routine on given buffer.
1822 */
1823int
1824ntfs_procfixups(
1825		struct ntfsmount * ntmp,
1826		u_int32_t magic,
1827		caddr_t buf,
1828		size_t len)
1829{
1830	struct fixuphdr *fhp = (struct fixuphdr *) buf;
1831	int             i;
1832	u_int16_t       fixup;
1833	u_int16_t      *fxp;
1834	u_int16_t      *cfxp;
1835
1836	if (fhp->fh_magic != magic) {
1837		printf("ntfs_procfixups: magic doesn't match: %08x != %08x\n",
1838		       fhp->fh_magic, magic);
1839		return (EINVAL);
1840	}
1841	if ((fhp->fh_fnum - 1) * ntmp->ntm_bps != len) {
1842		printf("ntfs_procfixups: " \
1843		       "bad fixups number: %d for %ld bytes block\n",
1844		       fhp->fh_fnum, (long)len);	/* XXX printf kludge */
1845		return (EINVAL);
1846	}
1847	if (fhp->fh_foff >= ntmp->ntm_spc * ntmp->ntm_mftrecsz * ntmp->ntm_bps) {
1848		printf("ntfs_procfixups: invalid offset: %x", fhp->fh_foff);
1849		return (EINVAL);
1850	}
1851	fxp = (u_int16_t *) (buf + fhp->fh_foff);
1852	cfxp = (u_int16_t *) (buf + ntmp->ntm_bps - 2);
1853	fixup = *fxp++;
1854	for (i = 1; i < fhp->fh_fnum; i++, fxp++) {
1855		if (*cfxp != fixup) {
1856			printf("ntfs_procfixups: fixup %d doesn't match\n", i);
1857			return (EINVAL);
1858		}
1859		*cfxp = *fxp;
1860		((caddr_t) cfxp) += ntmp->ntm_bps;
1861	}
1862	return (0);
1863}
1864
1865#if UNUSED_CODE
1866int
1867ntfs_runtocn(
1868	     cn_t * cn,
1869	     struct ntfsmount * ntmp,
1870	     u_int8_t * run,
1871	     u_long len,
1872	     cn_t vcn)
1873{
1874	cn_t            ccn = 0;
1875	cn_t            ccl = 0;
1876	u_long          off = 0;
1877	int             error = 0;
1878
1879#if NTFS_DEBUG
1880	int             i;
1881	printf("ntfs_runtocn: run: 0x%p, %ld bytes, vcn:%ld\n",
1882		run, len, (u_long) vcn);
1883	printf("ntfs_runtocn: run: ");
1884	for (i = 0; i < len; i++)
1885		printf("0x%02x ", run[i]);
1886	printf("\n");
1887#endif
1888
1889	if (NULL == run) {
1890		printf("ntfs_runtocn: run == NULL\n");
1891		return (EINVAL);
1892	}
1893	do {
1894		if (run[off] == 0) {
1895			printf("ntfs_runtocn: vcn too big\n");
1896			return (E2BIG);
1897		}
1898		vcn -= ccl;
1899		error = ntfs_parserun(&ccn, &ccl, run, len, &off);
1900		if (error) {
1901			printf("ntfs_runtocn: ntfs_parserun failed\n");
1902			return (error);
1903		}
1904	} while (ccl <= vcn);
1905	*cn = ccn + vcn;
1906	return (0);
1907}
1908#endif
1909
1910/*
1911 * this initializes toupper table & dependant variables to be ready for
1912 * later work
1913 */
1914void
1915ntfs_toupper_init()
1916{
1917	ntfs_toupper_tab = (wchar *) NULL;
1918	lockinit(&ntfs_toupper_lock, PVFS, "ntfs_toupper", 0, 0);
1919	ntfs_toupper_usecount = 0;
1920}
1921
1922void
1923ntfs_toupper_destroy(void)
1924{
1925
1926	lockdestroy(&ntfs_toupper_lock);
1927}
1928
1929/*
1930 * if the ntfs_toupper_tab[] is filled already, just raise use count;
1931 * otherwise read the data from the filesystem we are currently mounting
1932 */
1933int
1934ntfs_toupper_use(mp, ntmp)
1935	struct mount *mp;
1936	struct ntfsmount *ntmp;
1937{
1938	int error = 0;
1939	struct vnode *vp;
1940
1941	/* get exclusive access */
1942	LOCKMGR(&ntfs_toupper_lock, LK_EXCLUSIVE, NULL);
1943
1944	/* only read the translation data from a file if it hasn't been
1945	 * read already */
1946	if (ntfs_toupper_tab)
1947		goto out;
1948
1949	/*
1950	 * Read in Unicode lowercase -> uppercase translation file.
1951	 * XXX for now, just the first 256 entries are used anyway,
1952	 * so don't bother reading more
1953	 */
1954	MALLOC(ntfs_toupper_tab, wchar *, 256 * sizeof(wchar),
1955		M_NTFSRDATA, M_WAITOK);
1956
1957	if ((error = VFS_VGET(mp, NTFS_UPCASEINO, &vp)))
1958		goto out;
1959	error = ntfs_readattr(ntmp, VTONT(vp), NTFS_A_DATA, NULL,
1960			0, 256*sizeof(wchar), (char *) ntfs_toupper_tab, NULL);
1961	vput(vp);
1962
1963    out:
1964	ntfs_toupper_usecount++;
1965	LOCKMGR(&ntfs_toupper_lock, LK_RELEASE, NULL);
1966	return (error);
1967}
1968
1969/*
1970 * lower the use count and if it reaches zero, free the memory
1971 * tied by toupper table
1972 */
1973void
1974ntfs_toupper_unuse()
1975{
1976	/* get exclusive access */
1977	LOCKMGR(&ntfs_toupper_lock, LK_EXCLUSIVE, NULL);
1978
1979	ntfs_toupper_usecount--;
1980	if (ntfs_toupper_usecount == 0) {
1981		FREE(ntfs_toupper_tab, M_NTFSRDATA);
1982		ntfs_toupper_tab = NULL;
1983	}
1984#ifdef DIAGNOSTIC
1985	else if (ntfs_toupper_usecount < 0) {
1986		panic("ntfs_toupper_unuse(): use count negative: %d\n",
1987			ntfs_toupper_usecount);
1988	}
1989#endif
1990
1991	/* release the lock */
1992	LOCKMGR(&ntfs_toupper_lock, LK_RELEASE, NULL);
1993}
1994
1995/*
1996 * maps the Unicode char to 8bit equivalent
1997 * XXX currently only gets lower 8bit from the Unicode char
1998 * and substitutes a '_' for it if the result would be '\0';
1999 * something better has to be definitely though out
2000 */
2001char
2002ntfs_u28(unichar)
2003  wchar unichar;
2004{
2005	return (char) NTFS_U28(unichar);
2006}
2007
2008