From bb91a31741c7e7bb078e950c05b074f0124fe842 Mon Sep 17 00:00:00 2001 From: semenu Date: Wed, 3 Feb 1999 04:07:38 +0000 Subject: First version. Reviewed by: David O'Brien --- sys/ntfs/ntfs.h | 301 ++++++++++ sys/ntfs/ntfs_compr.c | 116 ++++ sys/ntfs/ntfs_compr.h | 33 ++ sys/ntfs/ntfs_extern.h | 29 + sys/ntfs/ntfs_ihash.c | 157 ++++++ sys/ntfs/ntfs_inode.h | 100 ++++ sys/ntfs/ntfs_subr.c | 1450 ++++++++++++++++++++++++++++++++++++++++++++++++ sys/ntfs/ntfs_subr.h | 96 ++++ sys/ntfs/ntfs_vfsops.c | 800 ++++++++++++++++++++++++++ sys/ntfs/ntfs_vnops.c | 1084 ++++++++++++++++++++++++++++++++++++ sys/ntfs/ntfsmount.h | 39 ++ 11 files changed, 4205 insertions(+) create mode 100644 sys/ntfs/ntfs.h create mode 100644 sys/ntfs/ntfs_compr.c create mode 100644 sys/ntfs/ntfs_compr.h create mode 100644 sys/ntfs/ntfs_extern.h create mode 100644 sys/ntfs/ntfs_ihash.c create mode 100644 sys/ntfs/ntfs_inode.h create mode 100644 sys/ntfs/ntfs_subr.c create mode 100644 sys/ntfs/ntfs_subr.h create mode 100644 sys/ntfs/ntfs_vfsops.c create mode 100644 sys/ntfs/ntfs_vnops.c create mode 100644 sys/ntfs/ntfsmount.h diff --git a/sys/ntfs/ntfs.h b/sys/ntfs/ntfs.h new file mode 100644 index 0000000..84bbc77 --- /dev/null +++ b/sys/ntfs/ntfs.h @@ -0,0 +1,301 @@ +/*- + * Copyright (c) 1998, 1999 Semen Ustimenko + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: ntfs.h,v 1.9 1999/02/02 01:54:54 semen Exp $ + */ + +#define DIAGNOSTIC +/*#define NTFS_DEBUG 1*/ + +typedef u_int64_t cn_t; +typedef u_int16_t wchar; + +#pragma pack(1) +#define BBSIZE 1024 +#define BBOFF ((off_t)(0)) +#define BBLOCK ((daddr_t)(0)) +#define NTFS_MFTINO 0 +#define NTFS_VOLUMEINO 3 +#define NTFS_ATTRDEFINO 4 +#define NTFS_ROOTINO 5 +#define NTFS_BITMAPINO 6 +#define NTFS_BOOTINO 7 +#define NTFS_BADCLUSINO 8 +#define NTFS_UPCASEINO 10 +#define NTFS_MAXFILENAME 255 + +struct fixuphdr { + u_int32_t fh_magic; + u_int16_t fh_foff; + u_int16_t fh_fnum; +}; + +#define NTFS_AF_INRUN 0x00000001 +struct attrhdr { + u_int32_t a_type; + u_int32_t reclen; + u_int8_t a_flag; + u_int8_t a_namelen; + u_int8_t a_nameoff; + u_int8_t reserved1; + u_int8_t a_compression; + u_int8_t reserved2; + u_int16_t a_index; +}; +#define NTFS_A_STD 0x10 +#define NTFS_A_ATTRLIST 0x20 +#define NTFS_A_NAME 0x30 +#define NTFS_A_VOLUMENAME 0x60 +#define NTFS_A_DATA 0x80 +#define NTFS_A_INDXROOT 0x90 +#define NTFS_A_INDX 0xA0 +#define NTFS_A_INDXBITMAP 0xB0 + +#define NTFS_MAXATTRNAME 255 +struct attr { + struct attrhdr a_hdr; + union { + struct { + u_int16_t a_datalen; + u_int16_t reserved1; + u_int16_t a_dataoff; + u_int16_t a_indexed; + } a_S_r; + struct { + cn_t a_vcnstart; + cn_t a_vcnend; + u_int16_t a_dataoff; + u_int16_t a_compressalg; + u_int32_t reserved1; + u_int64_t a_allocated; + u_int64_t a_datalen; + u_int64_t a_initialized; + } a_S_nr; + } a_S; +}; +#define a_r a_S.a_S_r +#define a_nr a_S.a_S_nr + +typedef struct { + u_int64_t t_create; + u_int64_t t_write; + u_int64_t t_mftwrite; + u_int64_t t_access; +} ntfs_times_t; + +#define NTFS_FFLAG_RDONLY 0x01LL +#define NTFS_FFLAG_HIDDEN 0x02LL +#define NTFS_FFLAG_SYSTEM 0x04LL +#define NTFS_FFLAG_ARCHIVE 0x20LL +#define NTFS_FFLAG_COMPRESSED 0x0800LL +#define NTFS_FFLAG_DIR 0x10000000LL + +struct attr_name { + u_int32_t n_pnumber; /* Parent ntnode */ + u_int32_t reserved; + ntfs_times_t n_times; + u_int64_t n_size; + u_int64_t n_attrsz; + u_int64_t n_flag; + u_int8_t n_namelen; + u_int8_t n_nametype; + u_int16_t n_name[1]; +}; + +#define NTFS_IRFLAG_INDXALLOC 0x00000001 +struct attr_indexroot { + u_int32_t ir_unkn1; /* always 0x30 */ + u_int32_t ir_unkn2; /* always 0x1 */ + u_int32_t ir_size;/* ??? */ + u_int32_t ir_unkn3; /* number of cluster */ + u_int32_t ir_unkn4; /* always 0x10 */ + u_int32_t ir_datalen; /* sizeof simething */ + u_int32_t ir_allocated; /* same as above */ + u_int16_t ir_flag;/* ?? always 1 */ + u_int16_t ir_unkn7; +}; + +struct attr_attrlist { + u_int32_t al_type; /* Attribute type */ + u_int16_t reclen; /* length of this entry */ + u_int8_t al_namelen; /* Attribute name len */ + u_int8_t al_nameoff; /* Name offset from entry start */ + u_int64_t al_vcnstart; /* VCN number */ + u_int32_t al_inumber; /* Parent ntnode */ + u_int32_t reserved; + u_int16_t al_index; /* Attribute index in MFT record */ + u_int16_t al_name[1]; /* Name */ +}; + +#define NTFS_INDXMAGIC (u_int32_t)(0x58444E49) +struct attr_indexalloc { + struct fixuphdr ia_fixup; + u_int64_t unknown1; + cn_t ia_bufcn; + u_int16_t ia_hdrsize; + u_int16_t unknown2; + u_int32_t ia_inuse; + u_int32_t ia_allocated; +}; + +#define NTFS_IEFLAG_SUBNODE 0x00000001 +#define NTFS_IEFLAG_LAST 0x00000002 + +struct attr_indexentry { + u_int32_t ie_number; + u_int32_t unknown1; + u_int16_t reclen; + u_int16_t ie_size; + u_int32_t ie_flag;/* 1 - has subnodes, 2 - last */ + u_int32_t ie_fpnumber; + u_int32_t unknown2; + ntfs_times_t ie_ftimes; + u_int64_t ie_fallocated; + u_int64_t ie_fsize; + u_int64_t ie_fflag; + u_int8_t ie_fnamelen; + u_int8_t ie_fnametype; + wchar ie_fname[NTFS_MAXFILENAME]; + /* cn_t ie_bufcn; buffer with subnodes */ +}; + +#define NTFS_FILEMAGIC (u_int32_t)(0x454C4946) +struct filerec { + struct fixuphdr fr_fixup; + u_int8_t reserved[8]; + u_int16_t fr_seqnum; /* Sequence number */ + u_int16_t fr_nlink; + u_int16_t fr_attroff; /* offset to attributes */ + u_int16_t fr_flags; /* 1-nonresident attr, 2-directory */ + u_int32_t fr_size;/* hdr + attributes */ + u_int32_t fr_allocated; /* allocated length of record */ + u_int64_t fr_mainrec; /* main record */ + u_int16_t fr_attrnum; /* maximum attr number + 1 ??? */ +}; + +#define NTFS_ADFLAG_NONRES 0x0080 /* Attrib can be non resident */ +#define NTFS_ADFLAG_INDEX 0x0002 /* Attrib can be indexed */ +struct attrdef { + wchar ad_name[0x40]; + u_int32_t ad_type; + u_int32_t reserved1[2]; + u_int32_t ad_flag; + u_int64_t ad_minlen; + u_int64_t ad_maxlen; /* -1 for nonlimited */ +}; + +struct ntvattrdef { + char ad_name[0x40]; + int ad_namelen; + u_int32_t ad_type; +}; + +struct bootfile { + u_int8_t reserved1[3]; /* asm jmp near ... */ + u_int8_t bf_sysid[8]; /* 'NTFS ' */ + u_int16_t bf_bps; /* bytes per sector */ + u_int8_t bf_spc; /* sectors per cluster */ + u_int8_t reserved2[7]; /* unused (zeroed) */ + u_int8_t bf_media; /* media desc. (0xF8) */ + u_int8_t reserved3[2]; + u_int16_t bf_spt; /* sectors per track */ + u_int16_t bf_heads; /* number of heads */ + u_int8_t reserver4[12]; + u_int64_t bf_spv; /* sectors per volume */ + cn_t bf_mftcn; /* $MFT cluster number */ + cn_t bf_mftmirrcn; /* $MFTMirr cn */ + u_int8_t bf_mftrecsz; /* MFT record size (clust) */ + /* 0xF6 inducates 1/4 */ + u_int32_t bf_ibsz; /* index buffer size */ + u_int32_t bf_volsn; /* volume ser. num. */ +}; + +#define NTFS_SYSNODESNUM 0x0B +struct ntfsmount { + struct mount *ntm_mountp; + struct bootfile ntm_bootfile; + dev_t ntm_dev; + struct vnode *ntm_devvp; + struct vnode *ntm_sysvn[NTFS_SYSNODESNUM]; + wchar *ntm_upcase; + u_int32_t ntm_bpmftrec; + uid_t ntm_uid; + gid_t ntm_gid; + mode_t ntm_mode; + u_long ntm_flag; + struct ntvattrdef *ntm_ad; + int ntm_adnum; +}; + +#define ntm_mftcn ntm_bootfile.bf_mftcn +#define ntm_mftmirrcn ntm_bootfile.bf_mftmirrcn +#define ntm_mftrecsz ntm_bootfile.bf_mftrecsz +#define ntm_spc ntm_bootfile.bf_spc +#define ntm_bps ntm_bootfile.bf_bps + +#pragma pack() + +#define NTFS_NEXTREC(s, type) ((type)(((caddr_t) s) + (s)->reclen)) + +/* Convert mount ptr to ntfsmount ptr. */ +#define VFSTONTFS(mp) ((struct ntfsmount *)((mp)->mnt_data)) +#define VTONT(v) ((struct ntnode *)((struct vnode *)(v)->v_data)) +#define NTTOV(i) (i->i_vnode) +#define ntfs_cntobn(cn) (daddr_t)((cn) * (ntmp->ntm_spc)) +#define ntfs_cntob(cn) (off_t)((cn) * (ntmp)->ntm_spc * (ntmp)->ntm_bps) +#define ntfs_btocn(off) (cn_t)((off) / ((ntmp)->ntm_spc * (ntmp)->ntm_bps)) +#define ntfs_btocl(off) (cn_t)((off + ntfs_cntob(1) - 1) / ((ntmp)->ntm_spc * (ntmp)->ntm_bps)) +#define ntfs_btocnoff(off) (off_t)((off) % ((ntmp)->ntm_spc * (ntmp)->ntm_bps)) +#define ntfs_bntob(bn) (daddr_t)((bn) * (ntmp)->ntm_bps) + +#define ntfs_bpbl (daddr_t)((ntmp)->ntm_bps) + +#if __FreeBSD_version >= 300000 +MALLOC_DECLARE(M_NTFSMNT); +MALLOC_DECLARE(M_NTFSNODE); +MALLOC_DECLARE(M_NTFSDIR); +MALLOC_DECLARE(M_NTFSIHASH); +#endif + +#if defined(NTFS_DEBUG) +#define dprintf(a) printf a +#if NTFS_DEBUG > 1 +#define ddprintf(a) printf a +#else +#define ddprintf(a) +#endif +#else +#define dprintf(a) +#define ddprintf(a) +#endif + +extern vop_t **ntfs_vnodeop_p; +struct ntnode; + +void ntfs_ihashinit __P((void)); +struct vnode *ntfs_ihashlookup __P((dev_t, ino_t)); +struct vnode *ntfs_ihashget __P((dev_t, ino_t)); +void ntfs_ihashins __P((struct ntnode *)); +void ntfs_ihashrem __P((register struct ntnode *)); diff --git a/sys/ntfs/ntfs_compr.c b/sys/ntfs/ntfs_compr.c new file mode 100644 index 0000000..a096ba6 --- /dev/null +++ b/sys/ntfs/ntfs_compr.c @@ -0,0 +1,116 @@ +/*- + * Copyright (c) 1998, 1999 Semen Ustimenko + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: ntfs_compr.c,v 1.4 1999/02/02 01:54:54 semen Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#define GET_UINT16(addr) (*((u_int16_t *)(addr))) + +int +ntfs_uncompblock( + u_int8_t * buf, + const u_int8_t * cbuf) +{ + u_int32_t ctag; + int len, dshift, lmask; + int blen, boff; + int i, j; + int pos, cpos; + + len = GET_UINT16(cbuf) & 0xFFF; + dprintf(("ntfs_uncompblock: block length: %d + 3, 0x%x,0x%04x\n", + len, len, GET_UINT16(cbuf))); + + if (!(GET_UINT16(cbuf) & 0x8000)) { + if ((len + 1) != NTFS_COMPBLOCK_SIZE) { + dprintf(("ntfs_uncompblock: len: %x instead of %d\n", + len, 0xfff)); + } + memcpy(buf, cbuf + 2, len + 1); + bzero(buf + len + 1, NTFS_COMPBLOCK_SIZE - 1 - len); + return len + 3; + } + cpos = 2; + pos = 0; + while ((cpos < len + 3) && (pos < NTFS_COMPBLOCK_SIZE)) { + ctag = cbuf[cpos++]; + for (i = 0; (i < 8) && (pos < NTFS_COMPBLOCK_SIZE); i++) { + if (ctag & 1) { + for (j = pos - 1, lmask = 0xFFF, dshift = 12; + j >= 0x10; j >>= 1) { + dshift--; + lmask >>= 1; + } + boff = -1 - (GET_UINT16(cbuf + cpos) >> dshift); + blen = 3 + (GET_UINT16(cbuf + cpos) & lmask); + for (j = 0; (j < blen) && (pos < NTFS_COMPBLOCK_SIZE); j++) { + buf[pos] = buf[pos + boff]; + pos++; + } + cpos += 2; + } else { + buf[pos++] = cbuf[cpos++]; + } + ctag >>= 1; + } + } + return len + 3; +} + +int +ntfs_uncompunit( + struct ntfsmount * ntmp, + u_int8_t * uup, + const u_int8_t * cup) +{ + int i; + int off = 0; + int new; + + for (i = 0; i * NTFS_COMPBLOCK_SIZE < ntfs_cntob(NTFS_COMPUNIT_CL); i++) { + new = ntfs_uncompblock(uup + i * NTFS_COMPBLOCK_SIZE, cup + off); + if (new == 0) + return (EINVAL); + off += new; + } + return (0); +} diff --git a/sys/ntfs/ntfs_compr.h b/sys/ntfs/ntfs_compr.h new file mode 100644 index 0000000..b2637c5 --- /dev/null +++ b/sys/ntfs/ntfs_compr.h @@ -0,0 +1,33 @@ +/*- + * Copyright (c) 1998, 1999 Semen Ustimenko + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: ntfs_compr.h,v 1.3 1999/01/28 03:56:06 semen Exp $ + */ + +#define NTFS_COMPBLOCK_SIZE 0x1000 +#define NTFS_COMPUNIT_CL 16 + +int ntfs_uncompblock(u_int8_t *, const u_int8_t *); +int ntfs_uncompunit(struct ntfsmount *, u_int8_t *, const u_int8_t *); diff --git a/sys/ntfs/ntfs_extern.h b/sys/ntfs/ntfs_extern.h new file mode 100644 index 0000000..b88ae7f --- /dev/null +++ b/sys/ntfs/ntfs_extern.h @@ -0,0 +1,29 @@ +/*- + * Copyright (c) 1998, 1999 Semen Ustimenko + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: ntfs_extern.h,v 1.3 1999/01/28 03:56:06 semen Exp $ + */ + +struct sockaddr; diff --git a/sys/ntfs/ntfs_ihash.c b/sys/ntfs/ntfs_ihash.c new file mode 100644 index 0000000..837f478 --- /dev/null +++ b/sys/ntfs/ntfs_ihash.c @@ -0,0 +1,157 @@ +/* + * Copyright (c) 1982, 1986, 1989, 1991, 1993, 1995 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ufs_ihash.c 8.7 (Berkeley) 5/17/95 + * $Id: ntfs_ihash.c,v 1.2 1999/01/02 01:17:38 semen Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +MALLOC_DEFINE(M_NTFSIHASH, "NTFS ihash", "NTFS Inode hash tables"); + +/* + * Structures associated with inode cacheing. + */ +static LIST_HEAD(ihashhead, ntnode) *ntfs_ihashtbl; +static u_long ntfs_ihash; /* size of hash table - 1 */ +#define NTNOHASH(device, inum) (&ntfs_ihashtbl[((device) + (inum)) & ntfs_ihash]) +static struct simplelock ntfs_ihash_slock; + +/* + * Initialize inode hash table. + */ +void +ntfs_ihashinit() +{ + + ntfs_ihashtbl = hashinit(desiredvnodes, M_NTFSIHASH, &ntfs_ihash); + simple_lock_init(&ntfs_ihash_slock); +} + +/* + * Use the device/inum pair to find the incore inode, and return a pointer + * to it. If it is in core, return it, even if it is locked. + */ +struct vnode * +ntfs_ihashlookup(dev, inum) + dev_t dev; + ino_t inum; +{ + struct ntnode *ip; + + simple_lock(&ntfs_ihash_slock); + for (ip = NTNOHASH(dev, inum)->lh_first; ip; ip = ip->i_hash.le_next) + if (inum == ip->i_number && dev == ip->i_dev) + break; + simple_unlock(&ntfs_ihash_slock); + + if (ip) + return (NTTOV(ip)); + return (NULLVP); +} + +/* + * Use the device/inum pair to find the incore inode, and return a pointer + * to it. If it is in core, but locked, wait for it. + */ +struct vnode * +ntfs_ihashget(dev, inum) + dev_t dev; + ino_t inum; +{ + struct proc *p = curproc; /* XXX */ + struct ntnode *ip; + struct vnode *vp; + +loop: + simple_lock(&ntfs_ihash_slock); + for (ip = NTNOHASH(dev, inum)->lh_first; ip; ip = ip->i_hash.le_next) { + if (inum == ip->i_number && dev == ip->i_dev) { + vp = NTTOV(ip); + simple_lock(&vp->v_interlock); + simple_unlock(&ntfs_ihash_slock); + if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, p)) + goto loop; + return (vp); + } + } + simple_unlock(&ntfs_ihash_slock); + return (NULL); +} + +/* + * Insert the inode into the hash table, and return it locked. + */ +void +ntfs_ihashins(ip) + struct ntnode *ip; +{ + struct proc *p = curproc; /* XXX */ + struct ihashhead *ipp; + + /* lock the inode, then put it on the appropriate hash list */ + lockmgr(&ip->i_lock, LK_EXCLUSIVE, (struct simplelock *)0, p); + + simple_lock(&ntfs_ihash_slock); + ipp = NTNOHASH(ip->i_dev, ip->i_number); + LIST_INSERT_HEAD(ipp, ip, i_hash); + ip->i_flag |= IN_HASHED; + simple_unlock(&ntfs_ihash_slock); +} + +/* + * Remove the inode from the hash table. + */ +void +ntfs_ihashrem(ip) + struct ntnode *ip; +{ + simple_lock(&ntfs_ihash_slock); + if (ip->i_flag & IN_HASHED) { + ip->i_flag &= ~IN_HASHED; + LIST_REMOVE(ip, i_hash); +#ifdef DIAGNOSTIC + ip->i_hash.le_next = NULL; + ip->i_hash.le_prev = NULL; +#endif + } + simple_unlock(&ntfs_ihash_slock); +} diff --git a/sys/ntfs/ntfs_inode.h b/sys/ntfs/ntfs_inode.h new file mode 100644 index 0000000..803ff86 --- /dev/null +++ b/sys/ntfs/ntfs_inode.h @@ -0,0 +1,100 @@ +/*- + * Copyright (c) 1998, 1999 Semen Ustimenko + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: ntfs_inode.h,v 1.8 1999/02/02 01:54:54 semen Exp $ + */ + +/* These flags are kept in i_flag. */ +#if __FreeBSD_version >= 300000 +#define IN_ACCESS 0x0001 /* Access time update request. */ +#define IN_CHANGE 0x0002 /* Inode change time update request. */ +#define IN_UPDATE 0x0004 /* Modification time update request. */ +#define IN_MODIFIED 0x0008 /* Inode has been modified. */ +#define IN_RENAME 0x0010 /* Inode is being renamed. */ +#define IN_SHLOCK 0x0020 /* File has shared lock. */ +#define IN_EXLOCK 0x0040 /* File has exclusive lock. */ +#define IN_HASHED 0x0080 /* Inode is on hash list */ +#define IN_LAZYMOD 0x0100 /* Modified, but don't write yet. */ +#else +#define IN_ACCESS 0x0001 /* Access time update request. */ +#define IN_CHANGE 0x0002 /* Inode change time update request. */ +#define IN_EXLOCK 0x0004 /* File has exclusive lock. */ +#define IN_LOCKED 0x0008 /* Inode lock. */ +#define IN_LWAIT 0x0010 /* Process waiting on file lock. */ +#define IN_MODIFIED 0x0020 /* Inode has been modified. */ +#define IN_RENAME 0x0040 /* Inode is being renamed. */ +#define IN_SHLOCK 0x0080 /* File has shared lock. */ +#define IN_UPDATE 0x0100 /* Modification time update request. */ +#define IN_WANTED 0x0200 /* Inode is wanted by a process. */ +#define IN_RECURSE 0x0400 /* Recursion expected */ +#endif + +#define IN_LOADED 0x8000 /* ntvattrs loaded */ +#define IN_PRELOADED 0x4000 /* loaded from directory entry */ +#define IN_AATTRNAME 0x2000 /* spaec allocated for i_defattrname */ + +struct ntnode { +#if __FreeBSD_version >= 300000 + struct lock i_lock; /* Must be first */ +#endif + LIST_ENTRY(ntnode) i_hash; + struct ntnode *i_next; + struct ntnode **i_prev; + struct vnode *i_vnode; + struct vnode *i_devvp; + struct ntfsmount *i_mp; + enum vtype i_type; + dev_t i_dev; + ino_t i_number; + u_int32_t i_flag; + + long i_nlink; /* MFR */ + ino_t i_mainrec; /* MFR */ + u_int32_t i_frflag; /* MFR */ + ntfs_times_t i_times; /* $NAME/dirinfo */ + ino_t i_pnumber; /* $NAME/dirinfo */ + u_int32_t i_fflag; /* $NAME/dirinfo */ + u_int64_t i_size; /* defattr/dirinfo: */ + u_int64_t i_allocated; /* defattr/dirinfo */ + + u_int32_t i_lastdattr; + u_int32_t i_lastdblnum; + u_int32_t i_lastdoff; + u_int32_t i_lastdnum; + caddr_t i_dirblbuf; + u_int32_t i_dirblsz; + + uid_t i_uid; + gid_t i_gid; + mode_t i_mode; + + u_int32_t i_defattr; + char *i_defattrname; + struct ntvattr *i_vattrp; + + int i_lockcount; /* Process lock count (recursion) */ + pid_t i_lockholder; /* DEBUG: holder of ntnode lock. */ + pid_t i_lockwaiter; /* DEBUG: waiter of ntnode lock. */ +}; diff --git a/sys/ntfs/ntfs_subr.c b/sys/ntfs/ntfs_subr.c new file mode 100644 index 0000000..629470f --- /dev/null +++ b/sys/ntfs/ntfs_subr.c @@ -0,0 +1,1450 @@ +/*- + * Copyright (c) 1998, 1999 Semen Ustimenko + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: ntfs_subr.c,v 1.9 1999/02/02 01:54:54 semen Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* #define NTFS_DEBUG 1 */ +#include +#include +#include +#include +#include + +#if __FreeBSD_version >= 300000 +MALLOC_DEFINE(M_NTFSNTVATTR, "NTFS vattr", "NTFS file attribute information"); +MALLOC_DEFINE(M_NTFSRDATA, "NTFS res data", "NTFS resident data"); +MALLOC_DEFINE(M_NTFSRUN, "NTFS vrun", "NTFS vrun storage"); +MALLOC_DEFINE(M_NTFSDECOMP, "NTFS decomp", "NTFS decompression temporary"); +#endif + +int +ntfs_ntvattrrele( + struct ntvattr * vap) +{ + dprintf(("ntfs_ntvattrrele: ino: %d, type: 0x%x\n", + vap->va_ip->i_number, vap->va_type)); + + vrele(NTTOV(vap->va_ip)); + + return (0); +} + +int +ntfs_ntvattrget( + struct ntfsmount * ntmp, + struct ntnode * ip, + u_int32_t type, + char *name, + cn_t vcn, + struct ntvattr ** vapp) +{ + int error; + struct ntvattr *vap; + struct ntvattr *lvap = NULL; + struct attr_attrlist *aalp; + struct attr_attrlist *nextaalp; + caddr_t alpool; + int len, namelen; + + *vapp = NULL; + + if (name) { + dprintf(("ntfs_ntvattrget: " \ + "ino: %d, type: 0x%x, name: %s, vcn: %d\n", \ + ip->i_number, type, name, (u_int32_t) vcn)); + namelen = strlen(name); + } else { + dprintf(("ntfs_ntvattrget: " \ + "ino: %d, type: 0x%x, vcn: %d\n", \ + ip->i_number, type, (u_int32_t) vcn)); + name = ""; + namelen = 0; + } + + if((ip->i_flag & IN_LOADED) == 0) { + dprintf(("ntfs_ntvattrget: node not loaded, ino: %d\n", + ip->i_number)); + error = ntfs_loadnode(ntmp,ip); + if(error) { + printf("ntfs_ntvattrget: FAILED TO LOAD INO: %d\n", + ip->i_number); + return (error); + } + } + + for (vap = ip->i_vattrp; vap; vap = vap->va_nextp) { + ddprintf(("type: 0x%x, vcn: %d - %d\n", \ + vap->va_type, (u_int32_t) vap->va_vcnstart, \ + (u_int32_t) vap->va_vcnend)); + if ((vap->va_type == type) && + (vap->va_vcnstart <= vcn) && (vap->va_vcnend >= vcn) && + (vap->va_namelen == namelen) && + (!strncmp(name, vap->va_name, namelen))) { + *vapp = vap; +#if __FreeBSD_version >= 300000 + VREF(NTTOV(vap->va_ip)); +#else + /* + * In RELENG_2_2 vref can call vfs_object_create(...) + * who calls vgetattr, who calls ntfs_getattr, who + * calls ntfs_ntvattrget, who calls vref... :-( This + * hack is to avoid it. XXX + */ + NTTOV(vap->va_ip)->v_usecount++; +#endif + return (0); + } + if (vap->va_type == NTFS_A_ATTRLIST) + lvap = vap; + } + + if (!lvap) { + dprintf(("ntfs_ntvattrget: UNEXISTED ATTRIBUTE: " \ + "ino: %d, type: 0x%x, name: %s, vcn: %d\n", \ + ip->i_number, type, name, (u_int32_t) vcn)); + return (ENOENT); + } + /* Scan $ATTRIBUTE_LIST for requested attribute */ + len = lvap->va_datalen; + MALLOC(alpool, caddr_t, len, M_TEMP, M_WAITOK); + error = ntfs_breadntvattr_plain(ntmp, ip, lvap, 0, len, alpool, &len); + if (error) + goto out; + + aalp = (struct attr_attrlist *) alpool; + nextaalp = NULL; + + while (len > 0) { + dprintf(("ntfs_ntvattrget: " \ + "attrlist: ino: %d, attr: 0x%x, vcn: %d\n", \ + aalp->al_inumber, aalp->al_type, \ + (u_int32_t) aalp->al_vcnstart)); + + if (len > aalp->reclen) { + nextaalp = NTFS_NEXTREC(aalp, struct attr_attrlist *); + } else { + nextaalp = NULL; + } + len -= aalp->reclen; + +#define AALPCMP(aalp,type,name,namelen) ( \ + (aalp->al_type == type) && (aalp->al_namelen == namelen) && \ + !uastrcmp(aalp->al_name,aalp->al_namelen,name,namelen) ) + + if (AALPCMP(aalp, type, name, namelen) && + (!nextaalp || (nextaalp->al_vcnstart > vcn) || + !AALPCMP(nextaalp, type, name, namelen))) { + struct vnode *newvp; + struct ntnode *newip; + + dprintf(("ntfs_ntvattrget: attrbute in ino: %d\n", + aalp->al_inumber)); + + error = VFS_VGET(ntmp->ntm_mountp, aalp->al_inumber, + &newvp); + if (error) { + printf("ntfs_ntvattrget: CAN'T VGET INO: %d\n", + aalp->al_inumber); + goto out; + } + newip = VTONT(newvp); + if(~newip->i_flag & IN_LOADED) { + dprintf(("ntfs_ntvattrget: node not loaded," \ + " ino: %d\n", newip->i_number)); + error = ntfs_loadnode(ntmp,ip); + if(error) { + printf("ntfs_ntvattrget: CAN'T LOAD " \ + "INO: %d\n", newip->i_number); + vput(newvp); + goto out; + } + } + for (vap = newip->i_vattrp; vap; vap = vap->va_nextp) { + if ((vap->va_type == type) && + (vap->va_vcnstart <= vcn) && + (vap->va_vcnend >= vcn) && + (vap->va_namelen == namelen) && + (!strncmp(name, vap->va_name, namelen))) { + *vapp = vap; +#if __FreeBSD_version >= 300000 + VREF(NTTOV(vap->va_ip)); +#else + /* See comment above */ + NTTOV(vap->va_ip)->v_usecount++; +#endif + vput(newvp); + error = 0; + goto out; + } + if (vap->va_type == NTFS_A_ATTRLIST) + lvap = vap; + } + printf("ntfs_ntvattrget: ATTRLIST ERROR.\n"); + vput(newvp); + break; + } +#undef AALPCMP + aalp = nextaalp; + } + error = ENOENT; + + dprintf(("ntfs_ntvattrget: UNEXISTED ATTRIBUTE: " \ + "ino: %d, type: 0x%x, name: %s, vcn: %d\n", \ + ip->i_number, type, name, (u_int32_t) vcn)); +out: + FREE(alpool, M_TEMP); + + return (error); +} + +int +ntfs_loadnode( + struct ntfsmount * ntmp, + struct ntnode * ip) +{ + struct filerec *mfrp; + daddr_t bn; + int error,off; + struct attr *ap; + struct ntvattr**vapp; + + dprintf(("ntfs_loadnode: loading ino: %d\n",ip->i_number)); + + MALLOC(mfrp, struct filerec *, ntfs_bntob(ntmp->ntm_bpmftrec), + M_TEMP, M_WAITOK); + + if (ip->i_number < NTFS_SYSNODESNUM) { + struct buf *bp; + + dprintf(("ntfs_loadnode: read system node\n")); + + bn = ntfs_cntobn(ntmp->ntm_mftcn) + + ntmp->ntm_bpmftrec * ip->i_number; + + error = bread(ntmp->ntm_devvp, + bn, ntfs_bntob(ntmp->ntm_bpmftrec), + NOCRED, &bp); + if (error) { + printf("ntfs_loadnode: BREAD FAILED\n"); + brelse(bp); + goto out; + } + memcpy(mfrp, bp->b_data, ntfs_bntob(ntmp->ntm_bpmftrec)); + bqrelse(bp); + } else { + struct vnode *vp; + + vp = ntmp->ntm_sysvn[NTFS_MFTINO]; + error = ntfs_breadattr(ntmp, VTONT(vp), NTFS_A_DATA, NULL, + ip->i_number * ntfs_bntob(ntmp->ntm_bpmftrec), + ntfs_bntob(ntmp->ntm_bpmftrec), mfrp); + if (error) { + printf("ntfs_loadnode: ntfs_breadattr failed\n"); + goto out; + } + } + /* Check if magic and fixups are correct */ + error = ntfs_procfixups(ntmp, NTFS_FILEMAGIC, (caddr_t)mfrp, + ntfs_bntob(ntmp->ntm_bpmftrec)); + if (error) { + printf("ntfs_loadnode: BAD MFT RECORD %d\n", + (u_int32_t) ip->i_number); + goto out; + } + + dprintf(("ntfs_loadnode: load attrs for ino: %d\n",ip->i_number)); + off = mfrp->fr_attroff; + ap = (struct attr *) ((caddr_t)mfrp + off); + vapp = &ip->i_vattrp; + while (ap->a_hdr.a_type != -1) { + error = ntfs_attrtontvattr(ntmp, vapp, ap); + if (error) + break; + (*vapp)->va_ip = ip; + vapp = &((*vapp)->va_nextp); + + off += ap->a_hdr.reclen; + ap = (struct attr *) ((caddr_t)mfrp + off); + } + if (error) { + printf("ntfs_loadnode: failed to load attr ino: %d\n", + ip->i_number); + goto out; + } + + ip->i_mainrec = mfrp->fr_mainrec; + ip->i_nlink = mfrp->fr_nlink; + ip->i_frflag = mfrp->fr_flags; + + ip->i_flag |= IN_LOADED; + + if (ip->i_mainrec == 0) { + struct ntvattr *vap; + + if (ntfs_ntvattrget(ntmp, ip, NTFS_A_NAME, NULL, 0, &vap) == 0){ + ip->i_times = vap->va_a_name->n_times; + ip->i_pnumber = vap->va_a_name->n_pnumber; + ip->i_fflag = vap->va_a_name->n_flag; + + ntfs_ntvattrrele(vap); + } + + if ((ip->i_fflag & NTFS_FFLAG_DIR) && (ip->i_defattr == 0)) { + struct ntvattr *irvap; + + ip->i_type = VDIR; + ip->i_defattr = NTFS_A_INDXROOT; + ip->i_defattrname = "$I30"; + error = ntfs_ntvattrget(ntmp, ip, + NTFS_A_INDXROOT, "$I30", + 0, &irvap); + if(error == 0) { + ip->i_dirblsz = irvap->va_a_iroot->ir_size; + MALLOC(ip->i_dirblbuf, caddr_t, + max(irvap->va_datalen,ip->i_dirblsz), + M_NTFSDIR, M_WAITOK); + + ntfs_ntvattrrele(irvap); + } + ip->i_size = 0; + ip->i_allocated = 0; + error = 0; + } else { + ip->i_type = VREG; + if(ip->i_defattr == 0) { + ip->i_defattr = NTFS_A_DATA; + ip->i_defattrname = NULL; + } + + ntfs_filesize(ntmp, ip, &ip->i_size, &ip->i_allocated); + } + } + + if (NTTOV(ip)) { + if (ip->i_number == NTFS_ROOTINO) + NTTOV(ip)->v_flag |= VROOT; + if (ip->i_number < NTFS_SYSNODESNUM) + NTTOV(ip)->v_flag |= VSYSTEM; + NTTOV(ip)->v_type = ip->i_type; + } +out: + FREE(mfrp, M_TEMP); + return (error); +} + + +int +ntfs_ntget( + struct ntfsmount * ntmp, + ino_t ino, + struct ntnode ** ipp) +{ + struct ntnode *ip; + + dprintf(("ntfs_ntget: allocate ntnode %d\n", ino)); + *ipp = NULL; + + MALLOC(ip, struct ntnode *, sizeof(struct ntnode), + M_NTFSNODE, M_WAITOK); + bzero((caddr_t) ip, sizeof(struct ntnode)); + + /* Generic initialization */ + ip->i_mp = ntmp; + ip->i_number = ino; + ip->i_dev = ntmp->ntm_dev; + ip->i_uid = ntmp->ntm_uid; + ip->i_gid = ntmp->ntm_gid; + ip->i_mode = ntmp->ntm_mode; + + /* Setup internal pointers */ + ip->i_vattrp = NULL; + ip->i_devvp = ntmp->ntm_devvp; + + *ipp = ip; + dprintf(("ntfs_ntget: allocated ntnode %d ok\n", ino)); + + return (0); +} + +void +ntfs_ntrele( + struct ntnode * ip) +{ + struct ntvattr *vap; + + dprintf(("ntfs_ntrele: rele ntnode %d\n", ip->i_number)); + while (ip->i_vattrp) { + vap = ip->i_vattrp; + ip->i_vattrp = vap->va_nextp; + ntfs_freentvattr(vap); + } + if(ip->i_flag & IN_AATTRNAME) FREE(ip->i_defattrname,M_TEMP); + dprintf(("ntfs_ntrele: rele ntnode %d ok\n", ip->i_number)); + FREE(ip, M_NTFSNODE); +} + +void +ntfs_freentvattr( + struct ntvattr * vap) +{ + if (vap->va_flag & NTFS_AF_INRUN) { + if (vap->va_vruncn) + FREE(vap->va_vruncn, M_NTFSRUN); + if (vap->va_vruncl) + FREE(vap->va_vruncl, M_NTFSRUN); + } else { + if (vap->va_datap) + FREE(vap->va_datap, M_NTFSRDATA); + } + FREE(vap, M_NTFSNTVATTR); +} + +int +ntfs_attrtontvattr( + struct ntfsmount * ntmp, + struct ntvattr ** rvapp, + struct attr * rap) +{ + int error, i; + struct ntvattr *vap; + + error = 0; + *rvapp = NULL; + + MALLOC(vap, struct ntvattr *, sizeof(*vap), M_NTFSNTVATTR, M_WAITOK); + vap->va_ip = NULL; + vap->va_flag = rap->a_hdr.a_flag; + vap->va_type = rap->a_hdr.a_type; + vap->va_compression = rap->a_hdr.a_compression; + vap->va_nextp = NULL; + vap->va_index = rap->a_hdr.a_index; + + ddprintf(("type: 0x%x, index: %d", vap->va_type, vap->va_index)); + + vap->va_namelen = rap->a_hdr.a_namelen; + if (rap->a_hdr.a_namelen) { + wchar *unp = (wchar *) ((caddr_t) rap + rap->a_hdr.a_nameoff); + ddprintf((", name:[")); + for (i = 0; i < vap->va_namelen; i++) { + vap->va_name[i] = unp[i]; + ddprintf(("%c", vap->va_name[i])); + } + ddprintf(("]")); + } + if (vap->va_flag & NTFS_AF_INRUN) { + ddprintf((", nonres.")); + vap->va_datalen = rap->a_nr.a_datalen; + vap->va_allocated = rap->a_nr.a_allocated; + vap->va_vcnstart = rap->a_nr.a_vcnstart; + vap->va_vcnend = rap->a_nr.a_vcnend; + vap->va_compressalg = rap->a_nr.a_compressalg; + error = ntfs_runtovrun(&(vap->va_vruncn), &(vap->va_vruncl), + &(vap->va_vruncnt), + (caddr_t) rap + rap->a_nr.a_dataoff); + } else { + vap->va_compressalg = 0; + ddprintf((", res.")); + vap->va_datalen = rap->a_r.a_datalen; + vap->va_allocated = rap->a_r.a_datalen; + vap->va_vcnstart = 0; + vap->va_vcnend = ntfs_btocn(vap->va_allocated); + MALLOC(vap->va_datap, caddr_t, vap->va_datalen, + M_NTFSRDATA, M_WAITOK); + memcpy(vap->va_datap, (caddr_t) rap + rap->a_r.a_dataoff, + rap->a_r.a_datalen); + } + ddprintf((", len: %d", vap->va_datalen)); + + if (error) + FREE(vap, M_NTFSNTVATTR); + else + *rvapp = vap; + + ddprintf(("\n")); + + return (error); +} + +int +ntfs_runtovrun( + cn_t ** rcnp, + cn_t ** rclp, + u_int32_t * rcntp, + u_int8_t * run) +{ + u_int32_t off; + u_int32_t sz, i; + cn_t *cn; + cn_t *cl; + u_int32_t cnt; + u_int64_t prev; + + off = 0; + cnt = 0; + i = 0; + while (run[off]) { + off += (run[off] & 0xF) + ((run[off] >> 4) & 0xF) + 1; + cnt++; + } + MALLOC(cn, cn_t *, cnt * sizeof(cn_t), M_NTFSRUN, M_WAITOK); + MALLOC(cl, cn_t *, cnt * sizeof(cn_t), M_NTFSRUN, M_WAITOK); + + off = 0; + cnt = 0; + prev = 0; + while (run[off]) { + u_int64_t tmp; + + sz = run[off++]; + cl[cnt] = 0; + + for (i = 0; i < (sz & 0xF); i++) + cl[cnt] += (u_int32_t) run[off++] << (i << 3); + + sz >>= 4; + if (run[off + sz - 1] & 0x80) { + tmp = ((u_int64_t) - 1) << (sz << 3); + for (i = 0; i < sz; i++) + tmp |= (u_int64_t) run[off++] << (i << 3); + } else { + tmp = 0; + for (i = 0; i < sz; i++) + tmp |= (u_int64_t) run[off++] << (i << 3); + } + if (tmp) + prev = cn[cnt] = prev + tmp; + else + cn[cnt] = tmp; + + cnt++; + } + *rcnp = cn; + *rclp = cl; + *rcntp = cnt; + return (0); +} + + +wchar +ntfs_toupper( + struct ntfsmount * ntmp, + wchar wc) +{ + return (ntmp->ntm_upcase[wc & 0xFF]); +} + +int +ntfs_uustricmp( + struct ntfsmount * ntmp, + wchar * str1, + int str1len, + wchar * str2, + int str2len) +{ + int i; + int res; + + for (i = 0; i < str1len && i < str2len; i++) { + res = (int) ntfs_toupper(ntmp, str1[i]) - + (int) ntfs_toupper(ntmp, str2[i]); + if (res) + return res; + } + return (str1len - str2len); +} + +int +ntfs_uastricmp( + struct ntfsmount * ntmp, + wchar * str1, + int str1len, + char *str2, + int str2len) +{ + int i; + int res; + + for (i = 0; i < str1len && i < str2len; i++) { + res = (int) ntfs_toupper(ntmp, str1[i]) - + (int) ntfs_toupper(ntmp, (wchar) str2[i]); + if (res) + return res; + } + return (str1len - str2len); +} + +int +ntfs_uastrcmp( + struct ntfsmount * ntmp, + wchar * str1, + int str1len, + char *str2, + int str2len) +{ + int i; + int res; + + for (i = 0; (i < str1len) && (i < str2len); i++) { + res = ((int) str1[i]) - ((int) str2[i]); + if (res) + return res; + } + return (str1len - str2len); +} + +int +ntfs_ntlookupattr( + struct ntfsmount * ntmp, + char * name, + int namelen, + int *type, + char **attrname) +{ + char *sys; + int syslen,i; + struct ntvattrdef *adp; + + if (namelen == 0) + return (0); + + if (name[0] == '$') { + sys = name; + for (syslen = 0; syslen < namelen; syslen++) { + if(sys[syslen] == ':') { + name++; + namelen--; + break; + } + } + name += syslen; + namelen -= syslen; + + adp = ntmp->ntm_ad; + for (i = 0; i < ntmp->ntm_adnum; i++){ + if((syslen == adp->ad_namelen) && + (!strncmp(sys,adp->ad_name,syslen))) { + *type = adp->ad_type; + if(namelen) { + MALLOC((*attrname), char *, namelen, + M_TEMP, M_WAITOK); + memcpy((*attrname), name, namelen); + (*attrname)[namelen] = '\0'; + }/* else + (*attrname) = NULL;*/ + return (0); + } + adp++; + } + return (ENOENT); + } + + if(namelen) { + MALLOC((*attrname), char *, namelen, M_TEMP, M_WAITOK); + memcpy((*attrname), name, namelen); + (*attrname)[namelen] = '\0'; + } + + return (0); +} +/* + * Lookup specifed node for filename, matching cnp, return filled ntnode. + */ +int +ntfs_ntlookup( + struct ntfsmount * ntmp, + struct ntnode * ip, + struct componentname * cnp, + struct ntnode ** ipp) +{ + struct ntvattr *vap; /* Root attribute */ + cn_t cn; /* VCN in current attribute */ + caddr_t rdbuf; /* Buffer to read directory's blocks */ + u_int32_t blsize; + u_int32_t rdsize; /* Length of data to read from current block */ + struct attr_indexentry *iep; + int error, res, anamelen, fnamelen; + char *fname,*aname; + u_int32_t aoff; + struct ntnode *nip; + + error = ntfs_ntvattrget(ntmp, ip, NTFS_A_INDXROOT, "$I30", 0, &vap); + if (error || (vap->va_flag & NTFS_AF_INRUN)) + return (ENOTDIR); + + blsize = vap->va_a_iroot->ir_size; + rdsize = vap->va_datalen; + + fname = cnp->cn_nameptr; + aname = NULL; + anamelen = 0; + for (fnamelen = 0; fnamelen < cnp->cn_namelen; fnamelen++) + if(fname[fnamelen] == ':') { + aname = fname + fnamelen + 1; + anamelen = cnp->cn_namelen - fnamelen - 1; + dprintf(("ntfs_ntlookup: file %s (%d), attr: %s (%d)\n", + fname, fnamelen, aname, anamelen)); + break; + } + + dprintf(("ntfs_ntlookup: blocksize: %d, rdsize: %d\n", blsize, rdsize)); + + MALLOC(rdbuf, caddr_t, blsize, M_TEMP, M_WAITOK); + + error = ntfs_breadattr(ntmp, ip, NTFS_A_INDXROOT, "$I30", + 0, rdsize, rdbuf); + if (error) + goto fail; + + aoff = sizeof(struct attr_indexroot); + + do { + iep = (struct attr_indexentry *) (rdbuf + aoff); + + while (!(iep->ie_flag & NTFS_IEFLAG_LAST) && (rdsize > aoff)) { + ddprintf(("scan: %d, %d\n", + (u_int32_t) iep->ie_number, + (u_int32_t) iep->ie_fnametype)); + res = ntfs_uastricmp(ntmp, iep->ie_fname, + iep->ie_fnamelen, fname, + fnamelen); + if (res == 0) { + /* Matched something (case ins.) */ + if (iep->ie_fnametype == 0 || + !(ntmp->ntm_flag & NTFS_MFLAG_CASEINS)) + res = ntfs_uastrcmp(ntmp, + iep->ie_fname, + iep->ie_fnamelen, + fname, + fnamelen); + if (res == 0) { + error = ntfs_ntget(ntmp, + iep->ie_number, + &nip); + if(error) + goto fail; + + nip->i_fflag = iep->ie_fflag; + nip->i_pnumber = iep->ie_fpnumber; + nip->i_times = iep->ie_ftimes; + + if(nip->i_fflag & NTFS_FFLAG_DIR) { + nip->i_type = VDIR; + nip->i_defattr = 0; + nip->i_defattrname = NULL; + } else { + nip->i_type = VREG; + nip->i_defattr = NTFS_A_DATA; + nip->i_defattrname = NULL; + } + if (aname) { + error = ntfs_ntlookupattr(ntmp, + aname, anamelen, + &nip->i_defattr, + &nip->i_defattrname); + if (error) { + ntfs_ntrele(nip); + goto fail; + } + + nip->i_type = VREG; + + if (nip->i_defattrname) + nip->i_flag |= IN_AATTRNAME; + } else { + /* Opening default attribute */ + nip->i_size = iep->ie_fsize; + nip->i_allocated = + iep->ie_fallocated; + nip->i_flag |= IN_PRELOADED; + } + *ipp = nip; + goto fail; + } + } else if (res > 0) + break; + + aoff += iep->reclen; + iep = (struct attr_indexentry *) (rdbuf + aoff); + } + + /* Dive if possible */ + if (iep->ie_flag & NTFS_IEFLAG_SUBNODE) { + dprintf(("ntfs_ntlookup: diving\n")); + + cn = *(cn_t *) (rdbuf + aoff + + iep->reclen - sizeof(cn_t)); + rdsize = blsize; + + error = ntfs_breadattr(ntmp, ip, NTFS_A_INDX, "$I30", + ntfs_cntob(cn), rdsize, rdbuf); + if (error) + goto fail; + + error = ntfs_procfixups(ntmp, NTFS_INDXMAGIC, + rdbuf, rdsize); + if (error) + goto fail; + + aoff = (((struct attr_indexalloc *) rdbuf)->ia_hdrsize + + 0x18); + } else { + dprintf(("ntfs_ntlookup: nowhere to dive :-(\n")); + error = ENOENT; + break; + } + } while (1); + + dprintf(("finish\n")); + +fail: + ntfs_ntvattrrele(vap); + FREE(rdbuf, M_TEMP); + return (error); +} + +int +ntfs_isnamepermitted( + struct ntfsmount * ntmp, + struct attr_indexentry * iep) +{ + + if (ntmp->ntm_flag & NTFS_MFLAG_ALLNAMES) + return 1; + + switch (iep->ie_fnametype) { + case 2: + ddprintf(("ntfs_isnamepermitted: skiped DOS name\n")); + return 0; + case 0: + case 1: + case 3: + return 1; + default: + printf("ntfs_isnamepermitted: " \ + "WARNING! Unknown file name type: %d\n", + iep->ie_fnametype); + break; + } + return 0; +} + +/* + * #undef dprintf #define dprintf(a) printf a + */ +int +ntfs_ntreaddir( + struct ntfsmount * ntmp, + struct ntnode * ip, + u_int32_t num, + struct attr_indexentry ** riepp) +{ + struct ntvattr *vap = NULL; /* IndexRoot attribute */ + struct ntvattr *bmvap = NULL; /* BitMap attribute */ + struct ntvattr *iavap = NULL; /* IndexAllocation attribute */ + caddr_t rdbuf; /* Buffer to read directory's blocks */ + u_char *bmp = NULL; /* Bitmap */ + u_int32_t blsize; /* Index allocation size (2048) */ + u_int32_t rdsize; /* Length of data to read */ + u_int32_t attrnum; /* Current attribute type */ + u_int32_t cpbl = 1; /* Clusters per directory block */ + u_int32_t blnum; + struct attr_indexentry *iep; + int error = ENOENT; + u_int32_t aoff, cnum; + + dprintf(("ntfs_ntreaddir: read ino: %d, num: %d\n", ip->i_number, num)); + error = ntfs_ntvattrget(ntmp, ip, NTFS_A_INDXROOT, "$I30", 0, &vap); + if (error) + return (ENOTDIR); + + blsize = ip->i_dirblsz; + rdbuf = ip->i_dirblbuf; + + dprintf(("ntfs_ntreaddir: rdbuf: 0x%p, blsize: %d\n", rdbuf, blsize)); + + if (vap->va_a_iroot->ir_flag & NTFS_IRFLAG_INDXALLOC) { + error = ntfs_ntvattrget(ntmp, ip, NTFS_A_INDXBITMAP, "$I30", + 0, &bmvap); + if (error) { + error = ENOTDIR; + goto fail; + } + MALLOC(bmp, u_char *, bmvap->va_datalen, M_TEMP, M_WAITOK); + error = ntfs_breadattr(ntmp, ip, NTFS_A_INDXBITMAP, "$I30", 0, + bmvap->va_datalen, bmp); + if (error) + goto fail; + + error = ntfs_ntvattrget(ntmp, ip, NTFS_A_INDX, "$I30", + 0, &iavap); + if (error) { + error = ENOTDIR; + goto fail; + } + cpbl = ntfs_btocn(blsize + ntfs_cntob(1) - 1); + dprintf(("ntfs_ntreaddir: indexalloc: %d, cpbl: %d\n", + iavap->va_datalen, cpbl)); + } else { + dprintf(("ntfs_ntreadidir: w/o BitMap and IndexAllocation\n")); + iavap = bmvap = NULL; + bmp = NULL; + } + + /* Try use previous values */ + if ((ip->i_lastdnum < num) && (ip->i_lastdnum != 0)) { + attrnum = ip->i_lastdattr; + aoff = ip->i_lastdoff; + blnum = ip->i_lastdblnum; + cnum = ip->i_lastdnum; + } else { + attrnum = NTFS_A_INDXROOT; + aoff = sizeof(struct attr_indexroot); + blnum = 0; + cnum = 0; + } + + do { + dprintf(("ntfs_ntreaddir: scan: 0x%x, %d, %d, %d, %d\n", + attrnum, (u_int32_t) blnum, cnum, num, aoff)); + rdsize = (attrnum == NTFS_A_INDXROOT) ? vap->va_datalen : blsize; + error = ntfs_breadattr(ntmp, ip, attrnum, "$I30", + ntfs_cntob(blnum * cpbl), rdsize, rdbuf); + if (error) + goto fail; + + if (attrnum == NTFS_A_INDX) { + error = ntfs_procfixups(ntmp, NTFS_INDXMAGIC, + rdbuf, rdsize); + if (error) + goto fail; + } + if (aoff == 0) + aoff = (attrnum == NTFS_A_INDX) ? + (0x18 + ((struct attr_indexalloc *) rdbuf)->ia_hdrsize) : + sizeof(struct attr_indexroot); + + iep = (struct attr_indexentry *) (rdbuf + aoff); + while (!(iep->ie_flag & NTFS_IEFLAG_LAST) && (rdsize > aoff)) { + if (ntfs_isnamepermitted(ntmp, iep)) { + if (cnum >= num) { + ip->i_lastdnum = cnum; + ip->i_lastdoff = aoff; + ip->i_lastdblnum = blnum; + ip->i_lastdattr = attrnum; + + *riepp = iep; + + error = 0; + goto fail; + } + cnum++; + } + aoff += iep->reclen; + iep = (struct attr_indexentry *) (rdbuf + aoff); + } + + if (iavap) { + if (attrnum == NTFS_A_INDXROOT) + blnum = 0; + else + blnum++; + + while (ntfs_cntob(blnum * cpbl) < iavap->va_datalen) { + if (bmp[blnum >> 3] & (1 << (blnum & 3))) + break; + blnum++; + } + + attrnum = NTFS_A_INDX; + aoff = 0; + if (ntfs_cntob(blnum * cpbl) >= iavap->va_datalen) + break; + dprintf(("ntfs_ntreaddir: blnum: %d\n", (u_int32_t) blnum)); + } + } while (iavap); + + *riepp = NULL; + ip->i_lastdnum = 0; + +fail: + if (vap) + ntfs_ntvattrrele(vap); + if (bmvap) + ntfs_ntvattrrele(bmvap); + if (iavap) + ntfs_ntvattrrele(iavap); + if (bmp) + FREE(bmp, M_TEMP); + return (error); +} +/* + * #undef dprintf #define dprintf(a) + */ + +struct timespec +ntfs_nttimetounix( + u_int64_t nt) +{ + struct timespec t; + + /* WindowNT times are in 100 ns and from 1601 Jan 1 */ + t.tv_nsec = (nt % (1000 * 1000 * 10)) * 100; + t.tv_sec = nt / (1000 * 1000 * 10) - + 369LL * 365LL * 24LL * 60LL * 60LL - + 89LL * 1LL * 24LL * 60LL * 60LL; + return (t); +} + +int +ntfs_times( + struct ntfsmount * ntmp, + struct ntnode * ip, + ntfs_times_t * tm) +{ + struct ntvattr *vap; + int error; + + dprintf(("ntfs_times: ino: %d...\n", ip->i_number)); + error = ntfs_ntvattrget(ntmp, ip, NTFS_A_NAME, NULL, 0, &vap); + if (error) + return (error); + *tm = vap->va_a_name->n_times; + ntfs_ntvattrrele(vap); + + return (0); +} + +int +ntfs_filesize( + struct ntfsmount * ntmp, + struct ntnode * ip, + u_int64_t * size, + u_int64_t * bytes) +{ + struct ntvattr *vap; + u_int64_t sz, bn; + int error; + + dprintf(("ntfs_filesize: ino: %d\n", ip->i_number)); + error = ntfs_ntvattrget(ntmp, ip, ip->i_defattr, ip->i_defattrname, + 0, &vap); + if (error) + return (error); + bn = vap->va_allocated; + sz = vap->va_datalen; + + dprintf(("ntfs_filesize: %d bytes (%d bytes allocated)\n", + (u_int32_t) sz, (u_int32_t) bn)); + + if (size) + *size = sz; + if (bytes) + *bytes = bn; + + ntfs_ntvattrrele(vap); + + return (0); +} + +int +ntfs_breadntvattr_plain( + struct ntfsmount * ntmp, + struct ntnode * ip, + struct ntvattr * vap, + off_t roff, + size_t rsize, + void *rdata, + size_t * initp) +{ + int error = 0; + int off; + + *initp = 0; + if (vap->va_flag & NTFS_AF_INRUN) { + int cnt; + cn_t ccn, ccl, cn, left, cl; + caddr_t data = rdata; + struct buf *bp; + size_t tocopy; + + ddprintf(("ntfs_breadntvattr_plain: data in run: %d chains\n", + vap->va_vruncnt)); + + off = roff; + left = rsize; + ccl = 0; + ccn = 0; + cnt = 0; + while (left && (cnt < vap->va_vruncnt)) { + ccn = vap->va_vruncn[cnt]; + ccl = vap->va_vruncl[cnt]; + + ddprintf(("ntfs_breadntvattr_plain: " \ + "left %d, cn: 0x%x, cl: %d, off: %d\n", \ + (u_int32_t) left, (u_int32_t) ccn, \ + (u_int32_t) ccl, (u_int32_t) off)); + + if (ntfs_cntob(ccl) < off) { + off -= ntfs_cntob(ccl); + cnt++; + continue; + } + if (ccn || ip->i_number == NTFS_BOOTINO) { /* XXX */ + ccl -= ntfs_btocn(off); + cn = ccn + ntfs_btocn(off); + off = ntfs_btocnoff(off); + + while (left && ccl) { + tocopy = min(left, + min(ntfs_cntob(ccl) - off, + MAXBSIZE - off)); + cl = ntfs_btocl(tocopy + off); + ddprintf(("ntfs_breadntvattr_plain: " \ + "read: cn: 0x%x cl: %d, " \ + "off: %d len: %d, left: %d\n", + (u_int32_t) cn, + (u_int32_t) cl, + (u_int32_t) off, + (u_int32_t) tocopy, + (u_int32_t) left)); + error = bread(ntmp->ntm_devvp, + ntfs_cntobn(cn), + ntfs_cntob(cl), + NOCRED, &bp); + if (error) { + brelse(bp); + return (error); + } + memcpy(data, bp->b_data + off, tocopy); + brelse(bp); + data = data + tocopy; + *initp += tocopy; + off = 0; + left -= tocopy; + cn += cl; + ccl -= cl; + } + } else { + tocopy = min(left, ntfs_cntob(ccl) - off); + ddprintf(("ntfs_breadntvattr_plain: " + "sparce: ccn: 0x%x ccl: %d, off: %d, " \ + " len: %d, left: %d\n", + (u_int32_t) ccn, (u_int32_t) ccl, + (u_int32_t) off, (u_int32_t) tocopy, + (u_int32_t) left)); + left -= tocopy; + off = 0; + bzero(data, tocopy); + data = data + tocopy; + } + cnt++; + } + if (left) { + printf("ntfs_breadntvattr_plain: POSSIBLE RUN ERROR\n"); + error = E2BIG; + } + } else { + ddprintf(("ntfs_breadnvattr_plain: data is in mft record\n")); + memcpy(rdata, vap->va_datap + roff, rsize); + *initp += rsize; + } + + return (error); +} + +int +ntfs_breadattr_plain( + struct ntfsmount * ntmp, + struct ntnode * ip, + u_int32_t attrnum, + char *attrname, + off_t roff, + size_t rsize, + void *rdata, + size_t * initp) +{ + size_t init; + int error = 0; + off_t off = roff, left = rsize, toread; + caddr_t data = rdata; + struct ntvattr *vap; + *initp = 0; + + while (left) { + error = ntfs_ntvattrget(ntmp, ip, attrnum, attrname, + ntfs_btocn(off), &vap); + if (error) + return (error); + toread = min(left, ntfs_cntob(vap->va_vcnend + 1) - off); + ddprintf(("ntfs_breadattr_plain: o: %d, s: %d (%d - %d)\n", + (u_int32_t) off, (u_int32_t) toread, + (u_int32_t) vap->va_vcnstart, + (u_int32_t) vap->va_vcnend)); + error = ntfs_breadntvattr_plain(ntmp, ip, vap, + off - ntfs_cntob(vap->va_vcnstart), + toread, data, &init); + if (error) { + printf("ntfs_breadattr_plain: " \ + "ntfs_breadntvattr_plain failed: o: %d, s: %d\n", + (u_int32_t) off, (u_int32_t) toread); + printf("ntfs_breadattr_plain: attrib: %d - %d\n", + (u_int32_t) vap->va_vcnstart, + (u_int32_t) vap->va_vcnend); + ntfs_ntvattrrele(vap); + break; + } + ntfs_ntvattrrele(vap); + left -= toread; + off += toread; + data = data + toread; + *initp += init; + } + + return (error); +} + +int +ntfs_breadattr( + struct ntfsmount * ntmp, + struct ntnode * ip, + u_int32_t attrnum, + char *attrname, + off_t roff, + size_t rsize, + void *rdata) +{ + int error = 0; + struct ntvattr *vap; + size_t init; + + ddprintf(("ntfs_breadattr: reading %d: 0x%x, from %d size %d bytes\n", + ip->i_number, attrnum, (u_int32_t) roff, (u_int32_t) rsize)); + + error = ntfs_ntvattrget(ntmp, ip, attrnum, attrname, 0, &vap); + if (error) + return (error); + + if ((roff > vap->va_datalen) || + (roff + rsize > vap->va_datalen)) { + ddprintf(("ntfs_breadattr: offset too big\n")); + ntfs_ntvattrrele(vap); + return (E2BIG); + } + if (vap->va_compression && vap->va_compressalg) { + u_int8_t *cup; + u_int8_t *uup; + off_t off = roff, left = rsize, tocopy; + caddr_t data = rdata; + cn_t cn; + + ddprintf(("ntfs_ntreadattr: compression: %d\n", + vap->va_compressalg)); + + MALLOC(cup, u_int8_t *, ntfs_cntob(NTFS_COMPUNIT_CL), + M_NTFSDECOMP, M_WAITOK); + MALLOC(uup, u_int8_t *, ntfs_cntob(NTFS_COMPUNIT_CL), + M_NTFSDECOMP, M_WAITOK); + + cn = (ntfs_btocn(roff)) & (~(NTFS_COMPUNIT_CL - 1)); + off = roff - ntfs_cntob(cn); + + while (left) { + error = ntfs_breadattr_plain(ntmp, ip, attrnum, + attrname, ntfs_cntob(cn), + ntfs_cntob(NTFS_COMPUNIT_CL), + cup, &init); + if (error) + break; + + tocopy = min(left, ntfs_cntob(NTFS_COMPUNIT_CL) - off); + + if (init == ntfs_cntob(NTFS_COMPUNIT_CL)) { + memcpy(data, cup + off, tocopy); + } else if (init == 0) { + bzero(data, tocopy); + } else { + error = ntfs_uncompunit(ntmp, uup, cup); + if (error) + break; + memcpy(data, uup + off, tocopy); + } + + left -= tocopy; + data = data + tocopy; + off += tocopy - ntfs_cntob(NTFS_COMPUNIT_CL); + cn += NTFS_COMPUNIT_CL; + } + + FREE(uup, M_NTFSDECOMP); + FREE(cup, M_NTFSDECOMP); + } else + error = ntfs_breadattr_plain(ntmp, ip, attrnum, attrname, + roff, rsize, rdata, &init); + ntfs_ntvattrrele(vap); + return (error); +} + +int +ntfs_parserun( + cn_t * cn, + cn_t * cl, + u_int8_t * run, + size_t len, + int *off) +{ + u_int8_t sz; + int i; + + if (NULL == run) { + printf("ntfs_runtocn: run == NULL\n"); + return (EINVAL); + } + sz = run[(*off)++]; + if (0 == sz) { + printf("ntfs_parserun: trying to go out of run\n"); + return (E2BIG); + } + *cl = 0; + if ((sz & 0xF) > 8 || (*off) + (sz & 0xF) > len) { + printf("ntfs_parserun: " \ + "bad run: length too big: %02x (%x < %x + sz)\n", + sz, len, *off); + return (EINVAL); + } + for (i = 0; i < (sz & 0xF); i++) + *cl += (u_int32_t) run[(*off)++] << (i << 3); + + sz >>= 4; + if ((sz & 0xF) > 8 || (*off) + (sz & 0xF) > len) { + printf("ntfs_parserun: " \ + "bad run: offset too big: %02x (%x < %x + sz)\n", + sz, len, *off); + return (EINVAL); + } + for (i = 0; i < (sz & 0xF); i++) + *cn += (u_int32_t) run[(*off)++] << (i << 3); + + return (0); +} + +int +ntfs_procfixups( + struct ntfsmount * ntmp, + u_int32_t magic, + caddr_t buf, + size_t len) +{ + struct fixuphdr *fhp = (struct fixuphdr *) buf; + int i; + u_int16_t fixup; + u_int16_t *fxp; + u_int16_t *cfxp; + + if (fhp->fh_magic != magic) { + printf("ntfs_procfixups: magic doesn't match: %08x != %08x\n", + fhp->fh_magic, magic); + return (EINVAL); + } + if ((fhp->fh_fnum - 1) * ntmp->ntm_bps != len) { + printf("ntfs_procfixups: " \ + "bad fixups number: %d for %d bytes block\n", + fhp->fh_fnum, len); + return (EINVAL); + } + if (fhp->fh_foff >= ntmp->ntm_spc * ntmp->ntm_mftrecsz * ntmp->ntm_bps) { + printf("ntfs_procfixups: invalid offset: %x", fhp->fh_foff); + return (EINVAL); + } + fxp = (u_int16_t *) (buf + fhp->fh_foff); + cfxp = (u_int16_t *) (buf + ntmp->ntm_bps - 2); + fixup = *fxp++; + for (i = 1; i < fhp->fh_fnum; i++, fxp++) { + if (*cfxp != fixup) { + printf("ntfs_procfixups: fixup %d doesn't match\n", i); + return (EINVAL); + } + *cfxp = *fxp; + ((caddr_t) cfxp) += ntmp->ntm_bps; + } + return (0); +} + +int +ntfs_runtocn( + cn_t * cn, + struct ntfsmount * ntmp, + u_int8_t * run, + size_t len, + cn_t vcn) +{ + cn_t ccn = 0; + cn_t ccl = 0; + int off = 0; + int error = 0; + +#if NTFS_DEBUG + int i; + printf("ntfs_runtocn: " \ + "run: 0x%p, %d bytes, vcn:%d\n", run, len, (u_int32_t) vcn); + printf("ntfs_runtocn: run: "); + for (i = 0; i < len; i++) + printf("0x%02x ", run[i]); + printf("\n"); +#endif + + if (NULL == run) { + printf("ntfs_runtocn: run == NULL\n"); + return (EINVAL); + } + do { + if (run[off] == 0) { + printf("ntfs_runtocn: vcn too big\n"); + return (E2BIG); + } + vcn -= ccl; + error = ntfs_parserun(&ccn, &ccl, run, len, &off); + if (error) { + printf("ntfs_runtocn: ntfs_parserun failed\n"); + return (error); + } + } while (ccl <= vcn); + *cn = ccn + vcn; + return (0); +} diff --git a/sys/ntfs/ntfs_subr.h b/sys/ntfs/ntfs_subr.h new file mode 100644 index 0000000..94cef19 --- /dev/null +++ b/sys/ntfs/ntfs_subr.h @@ -0,0 +1,96 @@ +/*- + * Copyright (c) 1998, 1999 Semen Ustimenko + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: ntfs_subr.h,v 1.3 1999/02/02 01:54:54 semen Exp $ + */ + +struct ntvattr { + struct ntvattr *va_nextp; + + u_int32_t va_vflag; + struct ntnode *va_ip; + + u_int32_t va_flag; + u_int32_t va_type; + u_int8_t va_namelen; + char va_name[NTFS_MAXATTRNAME]; + + u_int32_t va_compression; + u_int32_t va_compressalg; + u_int32_t va_datalen; + u_int32_t va_allocated; + cn_t va_vcnstart; + cn_t va_vcnend; + u_int16_t va_index; + union { + struct { + cn_t * cn; + cn_t * cl; + u_int32_t cnt; + } vrun; + caddr_t datap; + struct attr_name *name; + struct attr_indexroot *iroot; + struct attr_indexalloc *ialloc; + } va_d; +}; +#define va_vruncn va_d.vrun.cn +#define va_vruncl va_d.vrun.cl +#define va_vruncnt va_d.vrun.cnt +#define va_datap va_d.datap +#define va_a_name va_d.name +#define va_a_iroot va_d.iroot +#define va_a_ialloc va_d.ialloc + + +#define uastrcmp(a,b,c,d) ntfs_uastrcmp(ntmp,a,b,c,d) + +int ntfs_procfixups __P(( struct ntfsmount *, u_int32_t, caddr_t, size_t )); +int ntfs_parserun __P(( cn_t *, cn_t *, u_int8_t *, size_t, int *)); +int ntfs_runtocn __P(( cn_t *, struct ntfsmount *, u_int8_t *, size_t, cn_t)); +int ntfs_breadntvattr_plain __P(( struct ntfsmount *, struct ntnode *, struct ntvattr *, off_t, size_t, void *,size_t *)); +int ntfs_breadattr_plain __P(( struct ntfsmount *, struct ntnode *, u_int32_t, char *, off_t, size_t, void *,size_t *)); +int ntfs_breadattr __P(( struct ntfsmount *, struct ntnode *, u_int32_t, char *, off_t, size_t, void *)); +int ntfs_filesize __P(( struct ntfsmount *, struct ntnode *, u_int64_t *, u_int64_t *)); +int ntfs_times __P(( struct ntfsmount *, struct ntnode *, ntfs_times_t *)); +struct timespec ntfs_nttimetounix __P(( u_int64_t )); +int ntfs_ntreaddir __P(( struct ntfsmount *, struct ntnode *, u_int32_t, struct attr_indexentry **)); +wchar ntfs_toupper __P(( struct ntfsmount *, wchar )); +int ntfs_uustricmp __P(( struct ntfsmount *, wchar *, int, wchar *, int )); +int ntfs_uastricmp __P(( struct ntfsmount *, wchar *, int, char *, int )); +int ntfs_uastrcmp __P(( struct ntfsmount *, wchar *, int, char *, int )); +int ntfs_runtovrun __P(( cn_t **, cn_t **, u_int32_t *, u_int8_t *)); +int ntfs_attrtontvattr __P(( struct ntfsmount *, struct ntvattr **, struct attr * )); +void ntfs_freentvattr __P(( struct ntvattr * )); +int ntfs_loadntvattrs __P(( struct ntfsmount *, struct vnode *, caddr_t, struct ntvattr **)); +struct ntvattr * ntfs_findntvattr __P(( struct ntfsmount *, struct ntnode *, u_int32_t, cn_t )); +int ntfs_ntlookup __P(( struct ntfsmount *, struct ntnode *, struct componentname *, struct ntnode **)); +int ntfs_isnamepermitted __P(( struct ntfsmount *, struct attr_indexentry * )); +int ntfs_ntvattrrele __P(( struct ntvattr * )); +int ntfs_ntvattrget __P(( struct ntfsmount *, struct ntnode *, u_int32_t, char *, cn_t , struct ntvattr **)); +int ntfs_ntget __P(( struct ntfsmount *, ino_t, struct ntnode **)); +void ntfs_ntrele __P(( struct ntnode *)); +int ntfs_loadnode __P(( struct ntfsmount *, struct ntnode * )); +int ntfs_ntlookupattr __P(( struct ntfsmount *, char *, int, int *, char **)); diff --git a/sys/ntfs/ntfs_vfsops.c b/sys/ntfs/ntfs_vfsops.c new file mode 100644 index 0000000..b3ccf0a --- /dev/null +++ b/sys/ntfs/ntfs_vfsops.c @@ -0,0 +1,800 @@ +/*- + * Copyright (c) 1998, 1999 Semen Ustimenko + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: ntfs_vfsops.c,v 1.9 1999/02/02 01:54:54 semen Exp $ + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +/*#define NTFS_DEBUG 1*/ +#include +#include +#include +#include +#include + +#if __FreeBSD_version >= 300000 +MALLOC_DEFINE(M_NTFSMNT, "NTFS mount", "NTFS mount structure"); +MALLOC_DEFINE(M_NTFSNODE,"NTFS node", "NTFS node information"); +MALLOC_DEFINE(M_NTFSDIR,"NTFS dir", "NTFS dir buffer"); +#endif + +static int ntfs_mount __P((struct mount *, char *, caddr_t, + struct nameidata *, struct proc *)); +static int ntfs_quotactl __P((struct mount *, int, uid_t, caddr_t, + struct proc *)); +static int ntfs_root __P((struct mount *, struct vnode **)); +static int ntfs_start __P((struct mount *, int, struct proc *)); +static int ntfs_statfs __P((struct mount *, struct statfs *, + struct proc *)); +static int ntfs_sync __P((struct mount *, int, struct ucred *, + struct proc *)); +static int ntfs_unmount __P((struct mount *, int, struct proc *)); +static int ntfs_vget __P((struct mount *mp, ino_t ino, + struct vnode **vpp)); +static int ntfs_mountfs __P((register struct vnode *, struct mount *, + struct ntfs_args *, struct proc *)); +static int ntfs_vptofh __P((struct vnode *, struct fid *)); + +#if __FreeBSD_version >= 300000 +static int ntfs_init __P((struct vfsconf *)); +static int ntfs_fhtovp __P((struct mount *, struct fid *, + struct sockaddr *, struct vnode **, + int *, struct ucred **)); +#else +static int ntfs_init __P((void)); +static int ntfs_fhtovp __P((struct mount *, struct fid *, + struct mbuf *, struct vnode **, + int *, struct ucred **)); +#endif + +#if __FreeBSD_version >= 300000 +static int +ntfs_init ( + struct vfsconf *vcp ) +#else +static int +ntfs_init () +#endif +{ + static first=1; + + if(!first) return (0); + first = 1; + + printf("ntfs_init(): \n"); + + ntfs_ihashinit(); + + return 0; +} + +static int +ntfs_mount ( + struct mount *mp, + char *path, + caddr_t data, + struct nameidata *ndp, + struct proc *p ) +{ + u_int size; + int err = 0; + struct vnode *devvp; + struct ntfs_args args; + + /* + * Use NULL path to flag a root mount + */ + if( path == NULL) { + /* + *** + * Mounting root file system + *** + */ + + /* Get vnode for root device*/ + if( bdevvp( rootdev, &rootvp)) + panic("ffs_mountroot: can't setup bdevvp for root"); + + /* + * FS specific handling + */ + mp->mnt_flag |= MNT_RDONLY; /* XXX globally applicable?*/ + + /* + * Attempt mount + */ + if( ( err = ntfs_mountfs(rootvp, mp, &args, p)) != 0) { + /* fs specific cleanup (if any)*/ + goto error_1; + } + + goto dostatfs; /* success*/ + + } + + /* + *** + * Mounting non-root file system or updating a file system + *** + */ + + /* copy in user arguments*/ + err = copyin(data, (caddr_t)&args, sizeof (struct ntfs_args)); + if (err) + goto error_1; /* can't get arguments*/ + + /* + * If updating, check whether changing from read-only to + * read/write; if there is no device name, that's all we do. + */ + if (mp->mnt_flag & MNT_UPDATE) { + printf("ntfs_mount(): MNT_UPDATE not supported\n"); + err = EINVAL; + goto error_1; + +#if 0 + ump = VFSTOUFS(mp); + fs = ump->um_fs; + err = 0; + if (fs->fs_ronly == 0 && (mp->mnt_flag & MNT_RDONLY)) { + flags = WRITECLOSE; + if (mp->mnt_flag & MNT_FORCE) + flags |= FORCECLOSE; + if (vfs_busy(mp)) { + err = EBUSY; + goto error_1; + } + err = ffs_flushfiles(mp, flags, p); + vfs_unbusy(mp); + } + if (!err && (mp->mnt_flag & MNT_RELOAD)) + err = ffs_reload(mp, ndp->ni_cnd.cn_cred, p); + if (err) { + goto error_1; + } + if (fs->fs_ronly && (mp->mnt_flag & MNT_WANTRDWR)) { + if (!fs->fs_clean) { + if (mp->mnt_flag & MNT_FORCE) { + printf("WARNING: %s was not properly dismounted.\n",fs->fs_fsmnt); + } else { + printf("WARNING: R/W mount of %s denied. Filesystem is not clean - run fsck.\n", + fs->fs_fsmnt); + err = EPERM; + goto error_1; + } + } + fs->fs_ronly = 0; + } + if (fs->fs_ronly == 0) { + fs->fs_clean = 0; + ffs_sbupdate(ump, MNT_WAIT); + } + /* if not updating name...*/ + if (args.fspec == 0) { + /* + * Process export requests. Jumping to "success" + * will return the vfs_export() error code. + */ + err = vfs_export(mp, &ump->um_export, &args.export); + goto success; + } +#endif + } + + /* + * Not an update, or updating the name: look up the name + * and verify that it refers to a sensible block device. + */ + NDINIT(ndp, LOOKUP, FOLLOW, UIO_USERSPACE, args.fspec, p); + err = namei(ndp); + if (err) { + /* can't get devvp!*/ + goto error_1; + } + + devvp = ndp->ni_vp; + + if (devvp->v_type != VBLK) { + err = ENOTBLK; + goto error_2; + } + if (major(devvp->v_rdev) >= nblkdev) { + err = ENXIO; + goto error_2; + } + if (mp->mnt_flag & MNT_UPDATE) { +#if 0 + /* + ******************** + * UPDATE + ******************** + */ + + if (devvp != ntmp->um_devvp) + err = EINVAL; /* needs translation */ + else + vrele(devvp); + /* + * Update device name only on success + */ + if( !err) { + /* Save "mounted from" info for mount point (NULL pad)*/ + copyinstr( args.fspec, + mp->mnt_stat.f_mntfromname, + MNAMELEN - 1, + &size); + bzero( mp->mnt_stat.f_mntfromname + size, MNAMELEN - size); + } +#endif + } else { + /* + ******************** + * NEW MOUNT + ******************** + */ + + /* + * Since this is a new mount, we want the names for + * the device and the mount point copied in. If an + * error occurs, the mountpoint is discarded by the + * upper level code. + */ + /* Save "last mounted on" info for mount point (NULL pad)*/ + copyinstr( path, /* mount point*/ + mp->mnt_stat.f_mntonname, /* save area*/ + MNAMELEN - 1, /* max size*/ + &size); /* real size*/ + bzero( mp->mnt_stat.f_mntonname + size, MNAMELEN - size); + + /* Save "mounted from" info for mount point (NULL pad)*/ + copyinstr( args.fspec, /* device name*/ + mp->mnt_stat.f_mntfromname, /* save area*/ + MNAMELEN - 1, /* max size*/ + &size); /* real size*/ + bzero( mp->mnt_stat.f_mntfromname + size, MNAMELEN - size); + + err = ntfs_mountfs(devvp, mp, &args, p); + } + if (err) { + goto error_2; + } + +dostatfs: + /* + * Initialize FS stat information in mount struct; uses both + * mp->mnt_stat.f_mntonname and mp->mnt_stat.f_mntfromname + * + * This code is common to root and non-root mounts + */ + (void)VFS_STATFS(mp, &mp->mnt_stat, p); + + goto success; + + +error_2: /* error with devvp held*/ + + /* release devvp before failing*/ + vrele(devvp); + +error_1: /* no state to back out*/ + +success: + return( err); +} + +/* + * Common code for mount and mountroot + */ +int +ntfs_mountfs(devvp, mp, argsp, p) + register struct vnode *devvp; + struct mount *mp; + struct ntfs_args *argsp; + struct proc *p; +{ + struct buf *bp; + struct ntfsmount *ntmp; + dev_t dev = devvp->v_rdev; + int error, ronly, ncount, i; + struct vnode *vp; + + /* + * Disallow multiple mounts of the same device. + * Disallow mounting of a device that is currently in use + * (except for root, which might share swap device for miniroot). + * Flush out any old buffers remaining from a previous use. + */ + error = vfs_mountedon(devvp); + if (error) + return (error); + ncount = vcount(devvp); + if (devvp->v_object) + ncount -= 1; + if (ncount > 1 && devvp != rootvp) + return (EBUSY); +#if __FreeBSD_version >= 300000 + vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, p); + error = vinvalbuf(devvp, V_SAVE, p->p_ucred, p, 0, 0); + VOP_UNLOCK(devvp, 0, p); +#else + error = vinvalbuf(devvp, V_SAVE, p->p_ucred, p, 0, 0); +#endif + if (error) + return (error); + + ronly = (mp->mnt_flag & MNT_RDONLY) != 0; + error = VOP_OPEN(devvp, ronly ? FREAD : FREAD|FWRITE, FSCRED, p); + if (error) + return (error); + + bp = NULL; + + error = bread(devvp, BBLOCK, BBSIZE, NOCRED, &bp); + if (error) + goto out; + ntmp = malloc( sizeof *ntmp, M_NTFSMNT, M_WAITOK ); + bzero( ntmp, sizeof *ntmp ); + bcopy( bp->b_data, &ntmp->ntm_bootfile, sizeof(struct bootfile) ); + brelse( bp ); + bp = NULL; + + { + int8_t cpr = ntmp->ntm_mftrecsz; + if( cpr > 0 ) + ntmp->ntm_bpmftrec = ntmp->ntm_spc * cpr; + else + ntmp->ntm_bpmftrec = (1 << (-cpr)) / ntmp->ntm_bps; + } + printf("ntfs_mountfs(): bps: %d, spc: %d, media: %x, mftrecsz: %d (%d sects)\n", + ntmp->ntm_bps,ntmp->ntm_spc,ntmp->ntm_bootfile.bf_media, + ntmp->ntm_mftrecsz,ntmp->ntm_bpmftrec); + printf("ntfs_mountfs(): mftcn: 0x%x|0x%x\n", + (u_int32_t)ntmp->ntm_mftcn,(u_int32_t)ntmp->ntm_mftmirrcn); + + ntmp->ntm_mountp = mp; + ntmp->ntm_dev = dev; + ntmp->ntm_devvp = devvp; + ntmp->ntm_uid = argsp->uid; + ntmp->ntm_gid = argsp->gid; + ntmp->ntm_mode = argsp->mode; + ntmp->ntm_flag = argsp->flag; + mp->mnt_data = (qaddr_t)ntmp; + + printf("ntfs_mountfs(): case-%s,%s uid: %d, gid: %d, mode: %o\n", + (ntmp->ntm_flag & NTFS_MFLAG_CASEINS)?"insens.":"sens.", + (ntmp->ntm_flag & NTFS_MFLAG_ALLNAMES)?" allnames,":"", + ntmp->ntm_uid, ntmp->ntm_gid, ntmp->ntm_mode); + + printf("ntfs_mountfs(): reading system nodes...\n"); + { + i = NTFS_MFTINO; + error = VFS_VGET(mp, i, &ntmp->ntm_sysvn[i]); + if(error) + goto out1; + VREF(ntmp->ntm_sysvn[i]); + vput(ntmp->ntm_sysvn[i]); + } + + MALLOC( ntmp->ntm_upcase, wchar *, 65536 * sizeof(wchar), + M_NTFSMNT, M_WAITOK); + + printf("ntfs_mountfs(): opening $UpCase\n"); + error = VFS_VGET(mp, NTFS_UPCASEINO, &vp ); + if(error) + goto out1; + printf("ntfs_mountfs(): reading $UpCase\n"); + error = ntfs_breadattr( ntmp, VTONT(vp), NTFS_A_DATA, NULL, + 0, 65536*sizeof(wchar), ntmp->ntm_upcase); + printf("ntfs_mountfs(): closing $UpCase\n"); + vput(vp); + if(error) + goto out1; + + { + int num,j; + struct attrdef ad; + + printf("ntfs_mountfs(): opening $AttrDef\n"); + error = VFS_VGET(mp, NTFS_ATTRDEFINO, &vp ); + if(error) + goto out1; + + for(num=0;;num++) { + error = ntfs_breadattr(ntmp, VTONT(vp), + NTFS_A_DATA, NULL, + num * sizeof(ad), sizeof(ad), + &ad); + if (error) + goto out1; + if (ad.ad_name[0] == 0) + break; + } + printf("ntfs_mountfs(): reading %d attrdefs\n",num); + + MALLOC(ntmp->ntm_ad, struct ntvattrdef *, + num * sizeof(struct ntvattrdef), + M_NTFSMNT, M_WAITOK); + + ntmp->ntm_adnum = num; + + for(i=0;intm_ad[i].ad_name[j] = ad.ad_name[j]; + } while(ad.ad_name[j++]); + ntmp->ntm_ad[i].ad_namelen = j - 1; + ntmp->ntm_ad[i].ad_type = ad.ad_type; + printf("ntfs_mountfs(): attribute: %s, type: 0x%x\n", + ntmp->ntm_ad[i].ad_name, + ntmp->ntm_ad[i].ad_type); + } + printf("ntfs_mountfs(): closing $AttrDef\n"); + vput(vp); + } + + mp->mnt_stat.f_fsid.val[0] = (long)dev; +#if __FreeBSD_version >= 300000 + mp->mnt_stat.f_fsid.val[1] = mp->mnt_vfc->vfc_typenum; +#else + mp->mnt_stat.f_fsid.val[1] = MOUNT_NTFS; +#endif + mp->mnt_maxsymlinklen = 0; + mp->mnt_flag |= MNT_LOCAL; +#if __FreeBSD_version >= 300000 + devvp->v_specmountpoint = mp; +#else + devvp->v_specflags |= SI_MOUNTEDON; +#endif + return (0); +out1: + for(i=0;intm_sysvn[i]) vrele(ntmp->ntm_sysvn[i]); +out: +#if __FreeBSD_version >= 300000 + devvp->v_specmountpoint = NULL; +#else + devvp->v_specflags |= SI_MOUNTEDON; +#endif + if (bp) + brelse(bp); + (void)VOP_CLOSE(devvp, ronly ? FREAD : FREAD|FWRITE, NOCRED, p); + return (error); +} + +static int +ntfs_start ( + struct mount *mp, + int flags, + struct proc *p ) +{ + printf("\nntfs_start():\n"); + return (0); +} + +static int +ntfs_unmount( + struct mount *mp, + int mntflags, + struct proc *p) +{ + register struct ntfsmount *ntmp; + int error, ronly = 0, flags, i; + + printf("ntfs_unmount: unmounting...\n"); + ntmp = VFSTONTFS(mp); + + flags = 0; + if(mntflags & MNT_FORCE) + flags |= FORCECLOSE; + + printf("ntfs_unmount: vflushing...\n"); + for(i=0;intm_sysvn[i]) vrele(ntmp->ntm_sysvn[i]); + error = vflush(mp,NULLVP,flags); + if (error) { + printf("ntfs_unmount: vflush failed: %d\n",error); + return (error); + } + +#if __FreeBSD_version >= 300000 + ntmp->ntm_devvp->v_specmountpoint = NULL; +#else + ntmp->ntm_devvp->v_specflags &= ~SI_MOUNTEDON; + + VOP_LOCK(ntmp->ntm_devvp); + vnode_pager_uncache(ntmp->ntm_devvp); + VOP_UNLOCK(ntmp->ntm_devvp); +#endif + + vinvalbuf(ntmp->ntm_devvp, V_SAVE, NOCRED, p, 0, 0); + error = VOP_CLOSE(ntmp->ntm_devvp, ronly ? FREAD : FREAD|FWRITE, + NOCRED, p); + + vrele(ntmp->ntm_devvp); + + printf("ntfs_umount: freeing memory...\n"); + mp->mnt_data = (qaddr_t)0; + mp->mnt_flag &= ~MNT_LOCAL; + FREE(ntmp->ntm_ad, M_NTFSMNT); + FREE(ntmp->ntm_upcase, M_NTFSMNT); + FREE(ntmp, M_NTFSMNT); + return (error); +} + +static int +ntfs_root( + struct mount *mp, + struct vnode **vpp ) +{ + struct vnode *nvp; + int error = 0; + + dprintf(("ntfs_root():\n")); + error = VFS_VGET(mp, (ino_t)NTFS_ROOTINO, &nvp); + if(error) { + printf("ntfs_root: VFS_VGET failed: %d\n",error); + return (error); + } + + *vpp = nvp; + return (0); +} + +static int +ntfs_quotactl ( + struct mount *mp, + int cmds, + uid_t uid, + caddr_t arg, + struct proc *p) +{ + printf("\nntfs_quotactl():\n"); + return EOPNOTSUPP; +} + +static int +ntfs_statfs( + struct mount *mp, + struct statfs *sbp, + struct proc *p) +{ + struct ntfsmount *ntmp = VFSTONTFS(mp); + u_int64_t mftsize,mftallocated,bmsize,bmallocated; + struct vnode *vp; + int error,j,i; + u_int8_t *tmp; + + dprintf(("ntfs_statfs():")); + + ntfs_filesize(ntmp, VTONT(ntmp->ntm_sysvn[NTFS_MFTINO]), + &mftsize, &mftallocated); + + error = VFS_VGET(mp, NTFS_BITMAPINO, &vp); + if(error) + return (error); + + ntfs_filesize(ntmp, VTONT(vp), &bmsize, &bmallocated); + + MALLOC(tmp, u_int8_t *, bmsize,M_TEMP, M_WAITOK); + + error = ntfs_breadattr(ntmp, VTONT(vp), NTFS_A_DATA, NULL, + 0, bmsize, tmp); + if(error) { + FREE(tmp, M_TEMP); + vput(vp); + return (error); + } + vput(vp); + + sbp->f_bfree = 0; + for(i=0;if_bfree++; + + FREE(tmp, M_TEMP); + +#if __FreeBSD_version >= 300000 + sbp->f_type = mp->mnt_vfc->vfc_typenum; +#else + sbp->f_type = MOUNT_NTFS; +#endif + sbp->f_bsize = ntmp->ntm_bps; + sbp->f_iosize = ntmp->ntm_bps * ntmp->ntm_spc; + sbp->f_blocks = ntmp->ntm_bootfile.bf_spv; + sbp->f_bfree = sbp->f_bavail = ntfs_cntobn(sbp->f_bfree); + sbp->f_ffree = sbp->f_bfree / ntmp->ntm_bpmftrec; + sbp->f_files = mftallocated / ntfs_bntob(ntmp->ntm_bpmftrec) + + sbp->f_ffree; + if (sbp != &mp->mnt_stat) { + bcopy((caddr_t)mp->mnt_stat.f_mntonname, + (caddr_t)&sbp->f_mntonname[0], MNAMELEN); + bcopy((caddr_t)mp->mnt_stat.f_mntfromname, + (caddr_t)&sbp->f_mntfromname[0], MNAMELEN); + } + + return (0); +} + +static int +ntfs_sync ( + struct mount *mp, + int waitfor, + struct ucred *cred, + struct proc *p) +{ + /*dprintf(("ntfs_sync():\n"));*/ + return (0); +} + +#if __FreeBSD_version >= 300000 +static int +ntfs_fhtovp( + struct mount *mp, + struct fid *fhp, + struct sockaddr *nam, + struct vnode **vpp, + int *exflagsp, + struct ucred **credanonp) +#else +static int +ntfs_fhtovp( + struct mount *mp, + struct fid *fhp, + struct mbuf *nam, + struct vnode **vpp, + int *exflagsp, + struct ucred **credanonp) +#endif +{ + printf("\ntfs_fhtovp():\n"); + return 0; +} + +static int +ntfs_vptofh( + struct vnode *vp, + struct fid *fhp) +{ + printf("ntfs_vptofh():\n"); + return EOPNOTSUPP; +} + +static int +ntfs_vget( + struct mount *mp, + ino_t ino, + struct vnode **vpp) +{ + int error=0; + struct vnode *vp; + register struct ntfsmount *ntmp; + struct ntnode *ip; + + dprintf(("ntfs_vget: ino: %d\n",ino)); + + ntmp = VFSTONTFS(mp); + + *vpp = NULL; + + dprintf(("ntfs_ntvget: ihashlookup\n")); + if( (*vpp = ntfs_ihashget(ntmp->ntm_dev, ino)) != NULL ) + return (0); + + error = ntfs_ntget(ntmp,ino,&ip); + if(error) { + printf("ntfs_vget: ntfs_ntget failed\n"); + return (error); + } + + error = getnewvnode(VT_NTFS, ntmp->ntm_mountp, ntfs_vnodeop_p, &vp); + if(error) { + /* XXX */ + ntfs_ntrele(ip); + return (error); + } + ip->i_vnode = vp; + vp->v_data = ip; + vp->v_type = ip->i_type; + + ntfs_ihashins(ip); + + VREF(ip->i_devvp); + + error = ntfs_loadnode(ntmp, ip); + if(error) { + printf("ntfs_vget: CAN'T LOAD ATTRIBUTES FOR INO: %d\n", + ip->i_number); + vput(vp); + return (error); + } + + *vpp = vp; + + return (0); +} + +#if __FreeBSD_version >= 300000 +static struct vfsops ntfs_vfsops = { + ntfs_mount, + ntfs_start, + ntfs_unmount, + ntfs_root, + ntfs_quotactl, + ntfs_statfs, + ntfs_sync, + ntfs_vget, + ntfs_fhtovp, + ntfs_vptofh, + ntfs_init, + NULL, + NULL +}; +VFS_SET(ntfs_vfsops, ntfs, 0); +#else +static struct vfsops ntfs_vfsops = { + ntfs_mount, + ntfs_start, + ntfs_unmount, + ntfs_root, + ntfs_quotactl, + ntfs_statfs, + ntfs_sync, + ntfs_vget, + ntfs_fhtovp, + ntfs_vptofh, + ntfs_init, +}; + +VFS_SET(ntfs_vfsops, ntfs, MOUNT_NTFS, 0); +#endif + + diff --git a/sys/ntfs/ntfs_vnops.c b/sys/ntfs/ntfs_vnops.c new file mode 100644 index 0000000..b5394fe --- /dev/null +++ b/sys/ntfs/ntfs_vnops.c @@ -0,0 +1,1084 @@ +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * John Heidemann of the UCLA Ficus project. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: ntfs_vnops.c,v 1.9 1999/02/02 01:54:55 semen Exp $ + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + + +/*#define NTFS_DEBUG 1*/ +#include +#include +#include +#include + +static int ntfs_bypass __P((struct vop_generic_args *ap)); +static int ntfs_read __P((struct vop_read_args *)); +static int ntfs_bwrite __P((struct vop_bwrite_args *ap)); +static int ntfs_getattr __P((struct vop_getattr_args *ap)); +static int ntfs_inactive __P((struct vop_inactive_args *ap)); +static int ntfs_print __P((struct vop_print_args *ap)); +static int ntfs_reclaim __P((struct vop_reclaim_args *ap)); +static int ntfs_strategy __P((struct vop_strategy_args *ap)); +#if __FreeBSD_version < 300000 +static int ntfs_islocked __P((struct vop_islocked_args *ap)); +static int ntfs_unlock __P((struct vop_unlock_args *ap)); +static int ntfs_lock __P((struct vop_lock_args *ap)); +#endif +static int ntfs_access __P((struct vop_access_args *ap)); +static int ntfs_open __P((struct vop_open_args *ap)); +static int ntfs_close __P((struct vop_close_args *ap)); +static int ntfs_readdir __P((struct vop_readdir_args *ap)); +static int ntfs_lookup __P((struct vop_lookup_args *ap)); +static int ntfs_bmap __P((struct vop_bmap_args *ap)); +static int ntfs_getpages __P((struct vop_getpages_args *ap)); +static int ntfs_fsync __P((struct vop_fsync_args *ap)); + +int ntfs_prtactive = 1; /* 1 => print out reclaim of active vnodes */ + +/* + * Vnode op for VM getpages. + */ +int +ntfs_getpages(ap) + struct vop_getpages_args *ap; +{ + int i, error, nextoff, size, toff, npages, count; + struct uio uio; + struct iovec iov; + vm_offset_t kva; + struct buf *bp; + struct vnode *vp; + struct proc *p; + struct ucred *cred; + struct ntfsmount *ntmp; + vm_page_t *pages; + + vp = ap->a_vp; + p = curproc; /* XXX */ + cred = curproc->p_ucred; /* XXX */ + ntmp = VFSTONTFS(vp->v_mount); + pages = ap->a_m; + count = ap->a_count; + + if (vp->v_object == NULL) { + printf("ntfs_getpages: called with non-merged cache vnode??\n"); + return VM_PAGER_ERROR; + } + + /* + * We use only the kva address for the buffer, but this is extremely + * convienient and fast. + */ +#if __FreeBSD_version >= 400000 + bp = getpbuf(NULL); +#else + bp = getpbuf(); +#endif + + npages = btoc(count); + kva = (vm_offset_t) bp->b_data; + pmap_qenter(kva, pages, npages); + + iov.iov_base = (caddr_t) kva; + iov.iov_len = count; + uio.uio_iov = &iov; + uio.uio_iovcnt = 1; + uio.uio_offset = IDX_TO_OFF(pages[0]->pindex); + uio.uio_resid = count; + uio.uio_segflg = UIO_SYSSPACE; + uio.uio_rw = UIO_READ; + uio.uio_procp = p; + + error = VOP_READ(vp, &uio, 0, cred); + pmap_qremove(kva, npages); + +#if __FreeBSD_version >= 400000 + relpbuf(bp,NULL); +#else + relpbuf(bp); +#endif + + if (error && (uio.uio_resid == count)) + return VM_PAGER_ERROR; + + size = count - uio.uio_resid; + + for (i = 0, toff = 0; i < npages; i++, toff = nextoff) { + vm_page_t m; + nextoff = toff + PAGE_SIZE; + m = pages[i]; + + m->flags &= ~PG_ZERO; + + if (nextoff <= size) { + m->valid = VM_PAGE_BITS_ALL; + m->dirty = 0; + } else { + int nvalid = ((size + DEV_BSIZE - 1) - toff) & ~(DEV_BSIZE - 1); + vm_page_set_validclean(m, 0, nvalid); + } + + if (i != ap->a_reqpage) { + /* + * Whether or not to leave the page activated is up in + * the air, but we should put the page on a page queue + * somewhere (it already is in the object). Result: + * It appears that emperical results show that + * deactivating pages is best. + */ + + /* + * Just in case someone was asking for this page we + * now tell them that it is ok to use. + */ + if (!error) { + if (m->flags & PG_WANTED) + vm_page_activate(m); + else + vm_page_deactivate(m); +#if __FreeBSD_version >= 300000 + vm_page_wakeup(m); +#endif + } else { + vnode_pager_freepage(m); + } + } + } + return 0; +} + +/* + * This is a noop, simply returning what one has been given. + */ +int +ntfs_bmap(ap) + struct vop_bmap_args /* { + struct vnode *a_vp; + daddr_t a_bn; + struct vnode **a_vpp; + daddr_t *a_bnp; + int *a_runp; + int *a_runb; + } */ *ap; +{ + if (ap->a_vpp != NULL) + *ap->a_vpp = ap->a_vp; + if (ap->a_bnp != NULL) + *ap->a_bnp = ap->a_bn; + if (ap->a_runp != NULL) + *ap->a_runp = 0; + if (ap->a_runb != NULL) + *ap->a_runb = 0; + return (0); +} + +static int +ntfs_read(ap) + struct vop_read_args /* { + struct vnode *a_vp; + struct uio *a_uio; + int a_ioflag; + struct ucred *a_cred; + } */ *ap; +{ + register struct vnode *vp = ap->a_vp; + register struct ntnode *ip = VTONT(vp); + struct uio *uio = ap->a_uio; + struct ntfsmount *ntmp = ip->i_mp; + u_int8_t *data; + u_int64_t toread; + int error; + + dprintf(("ntfs_read: ino: %d, off: %d resid: %d, segflg: %d\n",ip->i_number,(u_int32_t)uio->uio_offset,uio->uio_resid,uio->uio_segflg)); + + ntfs_filesize( ntmp, ip, &toread, NULL ); + dprintf(("ntfs_read: filesize: %d",(u_int32_t)toread)); + + toread = min( uio->uio_resid, toread - uio->uio_offset ); + + dprintf((", toread: %d\n",(u_int32_t)toread)); + + MALLOC(data, u_int8_t *, toread, M_TEMP,M_WAITOK); + + error = ntfs_breadattr( ntmp, ip, ip->i_defattr, ip->i_defattrname, + uio->uio_offset, toread, data ); + if(error) { + printf("ntfs_read: ntfs_breadattr failed: %d\n",error); + FREE(data, M_TEMP); + return (error); + } + + error = uiomove(data, (int) toread, uio); + if(error) { + FREE(data, M_TEMP); + return (error); + } + + FREE(data, M_TEMP); + + return (0); +} + +static int +ntfs_bypass(ap) + struct vop_generic_args /* { + struct vnodeop_desc *a_desc; + + } */ *ap; +{ + int error = ENOTTY; + dprintf (("ntfs_bypass: %s\n", ap->a_desc->vdesc_name)); + return (error); +} + + +static int +ntfs_getattr(ap) + struct vop_getattr_args /* { + struct vnode *a_vp; + struct vattr *a_vap; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap; +{ + register struct vnode *vp = ap->a_vp; + register struct ntnode *ip = VTONT(vp); + register struct vattr *vap = ap->a_vap; + int error; + + dprintf(("ntfs_getattr: %d, flags: %d\n",ip->i_number,ip->i_flag)); + + if ((ip->i_flag & (IN_LOADED | IN_PRELOADED)) == 0) { + error = ntfs_loadnode(ip->i_mp,ip); + if (error) + return error; + } + vap->va_fsid = ip->i_dev; + vap->va_fileid = ip->i_number; + vap->va_mode = ip->i_mode; + vap->va_nlink = ip->i_nlink; + vap->va_uid = ip->i_uid; + vap->va_gid = ip->i_gid; + vap->va_rdev = (dev_t)0; + vap->va_size = ip->i_size; + vap->va_bytes = ip->i_allocated; + vap->va_atime = ntfs_nttimetounix(ip->i_times.t_access); + vap->va_mtime = ntfs_nttimetounix(ip->i_times.t_write); + vap->va_ctime = ntfs_nttimetounix(ip->i_times.t_create); + vap->va_flags = ip->i_flag; + vap->va_gen = 0; + vap->va_blocksize = ip->i_mp->ntm_spc * ip->i_mp->ntm_bps; + vap->va_type = ip->i_type; + vap->va_filerev = 0; + return (0); +} + + +/* + * Last reference to an ntnode. If necessary, write or delete it. + */ +int +ntfs_inactive(ap) + struct vop_inactive_args /* { + struct vnode *a_vp; + } */ *ap; +{ + register struct vnode *vp = ap->a_vp; +#if defined(NTFS_DEBUG) || defined(DISGNOSTIC) + register struct ntnode *ip = VTONT(vp); +#endif + int error; + + dprintf(("ntfs_inactive: %d (%d locks)\n", + ip->i_number,ip->i_lockcount)); + + if (ntfs_prtactive && vp->v_usecount != 0) + vprint("ntfs_inactive: pushing active", vp); + + error = 0; + +#if __FreeBSD_version >= 300000 + VOP_UNLOCK(vp,0,ap->a_p); +#else +#ifdef DIAGNOSTIC + if (VOP_ISLOCKED(vp)) + panic("ntfs_inactive: locked ntnode"); + if (curproc) + ip->i_lockholder = curproc->p_pid; + else + ip->i_lockholder = -1; +#endif + ip->i_flag |= IN_LOCKED; + VOP_UNLOCK(vp); +#endif + /* + * If we are done with the ntnode, reclaim it + * so that it can be reused immediately. + */ + if (vp->v_usecount == 0 /*&& ip->i_mode == 0*/) +#if __FreeBSD_version >= 300000 + vrecycle(vp, (struct simplelock *)0, ap->a_p); +#else + vgone(vp); +#endif + return (error); +} + +/* + * Reclaim an inode so that it can be used for other purposes. + */ +int +ntfs_reclaim(ap) + struct vop_reclaim_args /* { + struct vnode *a_vp; + } */ *ap; +{ + register struct vnode *vp = ap->a_vp; + register struct ntnode *ip = VTONT(vp); + + dprintf(("ntfs_reclaim: reclaim: %d\n",ip->i_number)); + +#if __FreeBSD_version >= 300000 + VOP_UNLOCK(vp,0,ap->a_p); +#endif + + if(ip->i_dirblbuf) { + FREE(ip->i_dirblbuf, M_NTFSDIR); + ip->i_dirblbuf = NULL; + } + + /* + * Remove the inode from its hash chain. + */ + ntfs_ihashrem(ip); + + /* + * Purge old data structures associated with the inode. + */ + cache_purge(vp); + if (ip->i_devvp) { + vrele(ip->i_devvp); + ip->i_devvp = 0; + } + + ntfs_ntrele(ip); + + vp->v_data = NULL; + + return (0); +} + +static int +ntfs_print(ap) + struct vop_print_args /* { + struct vnode *a_vp; + } */ *ap; +{ +/* printf("[ntfs_print]");*/ + + return (0); +} + +/* + * Calculate the logical to physical mapping if not done already, + * then call the device strategy routine. + */ +int +ntfs_strategy(ap) + struct vop_strategy_args /* { + struct buf *a_bp; + } */ *ap; +{ + register struct buf *bp = ap->a_bp; + struct ucred *cr; + struct proc *p; + int error = 0; + + dprintf(("strategy: data: %p, npages: %d,dirty: %d\n",bp->b_data,bp->b_npages,bp->b_dirtyend)); + if (bp->b_flags & B_PHYS) + panic("ntfs physio"); + if (bp->b_flags & B_ASYNC) + p = (struct proc *)0; + else + p = curproc; /* XXX */ + if (bp->b_flags & B_READ) + cr = bp->b_rcred; + else + cr = bp->b_wcred; + /* + * If the op is asynchronous and an i/o daemon is waiting + * queue the request, wake it up and wait for completion + * otherwise just do it ourselves. + */ +/* + if ((bp->b_flags & B_ASYNC) == 0 || + nfs_asyncio(bp, NOCRED)) + error = nfs_doio(bp, cr, p); +*/ + + return (ENOTTY); + return (error); +} + +static int +ntfs_bwrite(ap) + struct vop_bwrite_args /* { + struct buf *a_bp; + } */ *ap; +{ + int error = ENOTTY; + + printf("ntfs_bwrite: \n"); + + return (error); +} + +#if __FreeBSD_version < 300000 +/* + * Check for a locked ntnode. + */ +int +ntfs_islocked(ap) + struct vop_islocked_args /* { + struct vnode *a_vp; + } */ *ap; +{ + register struct ntnode *ip = VTONT(ap->a_vp); + + dprintf(("ntfs_islocked %d (%d locks)\n",ip->i_number,ip->i_lockcount)); + + if (ip->i_flag & IN_LOCKED) + return (1); + return (0); +} + +/* + * Unlock an ntnode. If WANT bit is on, wakeup. + */ +int ntfs_lockcount = 90; +int +ntfs_unlock(ap) + struct vop_unlock_args /* { + struct vnode *a_vp; + } */ *ap; +{ + register struct ntnode *ip = VTONT(ap->a_vp); + struct proc *p = curproc; + + dprintf(("ntfs_unlock %d (%d locks)\n",ip->i_number,ip->i_lockcount)); + +#ifdef DIAGNOSTIC + + if ((ip->i_flag & IN_LOCKED) == 0) { + vprint("ntfs_unlock: unlocked ntnode", ap->a_vp); + panic("ntfs_unlock NOT LOCKED"); + } + if (p && p->p_pid != ip->i_lockholder && p->p_pid > -1 && + ip->i_lockholder > -1 && ntfs_lockcount++ < 100) + panic("unlocker (%d) != lock holder (%d)", + p->p_pid, ip->i_lockholder); +#endif + + if (--ip->i_lockcount > 0) { + if ((ip->i_flag & IN_RECURSE) == 0) + panic("ntfs_unlock: recursive lock prematurely released, pid=%d\n", ip->i_lockholder); + return (0); + } + ip->i_lockholder = 0; + ip->i_flag &= ~(IN_LOCKED|IN_RECURSE); + if (ip->i_flag & IN_WANTED) { + ip->i_flag &= ~IN_WANTED; + wakeup((caddr_t)ip); + } + return (0); +} + +/* + * Lock an ntnode. If its already locked, set the WANT bit and sleep. + */ +int +ntfs_lock(ap) + struct vop_lock_args /* { + struct vnode *a_vp; + } */ *ap; +{ + struct proc *p = curproc; + register struct vnode *vp = ap->a_vp; + register struct ntnode *ip = VTONT(vp); + + dprintf(("ntfs_lock %d (%d locks)\n",ip->i_number,ip->i_lockcount)); + +start: + while (vp->v_flag & VXLOCK) { + vp->v_flag |= VXWANT; + (void) tsleep((caddr_t)vp, PINOD, "ntflk1", 0); + } + if (vp->v_tag == VT_NON) + return (ENOENT); + ip = VTONT(vp); + if (ip->i_flag & IN_LOCKED) { + if (p->p_pid == ip->i_lockholder) { + if( (ip->i_flag & IN_RECURSE) == 0) + panic("ntfs_lock: recursive lock not expected, pid: %d\n", + ip->i_lockholder); + } else { + ip->i_flag |= IN_WANTED; +#ifdef DIAGNOSTIC + if (p) + ip->i_lockwaiter = p->p_pid; + else + ip->i_lockwaiter = -1; +#endif + (void) tsleep((caddr_t)ip, PINOD, "ntflk2", 0); + goto start; + } + } +#ifdef DIAGNOSTIC + ip->i_lockwaiter = 0; + if (((ip->i_flag & IN_RECURSE) == 0) && (ip->i_lockholder != 0)) + panic("lockholder (%d) != 0", ip->i_lockholder); + if (p && p->p_pid == 0) + printf("locking by process 0\n"); +#endif + + if ((ip->i_flag & IN_RECURSE) == 0) + ip->i_lockcount = 1; + else + ++ip->i_lockcount; + + if (p) + ip->i_lockholder = p->p_pid; + else + ip->i_lockholder = -1; + ip->i_flag |= IN_LOCKED; + return (0); +} +#endif + +int +ntfs_access(ap) + struct vop_access_args /* { + struct vnode *a_vp; + int a_mode; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap; +{ + struct vnode *vp = ap->a_vp; + struct ntnode *ip = VTONT(vp); + struct ucred *cred = ap->a_cred; + mode_t mask, mode = ap->a_mode; + register gid_t *gp; + int i; +#ifdef QUOTA + int error; +#endif + + dprintf(("ntfs_access: %d\n",ip->i_number)); + + /* + * Disallow write attempts on read-only file systems; + * unless the file is a socket, fifo, or a block or + * character device resident on the file system. + */ + if (mode & VWRITE) { + switch (vp->v_type) { + case VDIR: + case VLNK: + case VREG: + if (vp->v_mount->mnt_flag & MNT_RDONLY) + return (EROFS); +#ifdef QUOTA + if (error = getinoquota(ip)) + return (error); +#endif + break; + } + } + + /* If immutable bit set, nobody gets to write it. */ +/* + if ((mode & VWRITE) && (ip->i_flags & IMMUTABLE)) + return (EPERM); +*/ + + /* Otherwise, user id 0 always gets access. */ + if (cred->cr_uid == 0) + return (0); + + mask = 0; + + /* Otherwise, check the owner. */ + if (cred->cr_uid == ip->i_uid) { + if (mode & VEXEC) + mask |= S_IXUSR; + if (mode & VREAD) + mask |= S_IRUSR; + if (mode & VWRITE) + mask |= S_IWUSR; + return ((ip->i_mode & mask) == mask ? 0 : EACCES); + } + + /* Otherwise, check the groups. */ + for (i = 0, gp = cred->cr_groups; i < cred->cr_ngroups; i++, gp++) + if (ip->i_gid == *gp) { + if (mode & VEXEC) + mask |= S_IXGRP; + if (mode & VREAD) + mask |= S_IRGRP; + if (mode & VWRITE) + mask |= S_IWGRP; + return ((ip->i_mode & mask) == mask ? 0 : EACCES); + } + + /* Otherwise, check everyone else. */ + if (mode & VEXEC) + mask |= S_IXOTH; + if (mode & VREAD) + mask |= S_IROTH; + if (mode & VWRITE) + mask |= S_IWOTH; + return ((ip->i_mode & mask) == mask ? 0 : EACCES); +} + +/* + * Open called. + * + * Nothing to do. + */ +/* ARGSUSED */ +static int +ntfs_open(ap) + struct vop_open_args /* { + struct vnode *a_vp; + int a_mode; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap; +{ +#if NTFS_DEBUG + register struct vnode *vp = ap->a_vp; + register struct ntnode *ip = VTONT(vp); + + printf("ntfs_open: %d (%d locks)\n",ip->i_number,ip->i_lockcount); +#endif + + /* + * Files marked append-only must be opened for appending. + */ + + return (0); +} + +/* + * Close called. + * + * Update the times on the inode. + */ +/* ARGSUSED */ +static int +ntfs_close(ap) + struct vop_close_args /* { + struct vnode *a_vp; + int a_fflag; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap; +{ +#if NTFS_DEBUG + register struct vnode *vp = ap->a_vp; + register struct ntnode *ip = VTONT(vp); + + printf("ntfs_close: %d (%d locks)\n",ip->i_number,ip->i_lockcount); +#endif + + return (0); +} + +/* +#undef dprintf +#define dprintf(a) printf a +*/ +int +ntfs_readdir(ap) + struct vop_readdir_args /* { + struct vnode *a_vp; + struct uio *a_uio; + struct ucred *a_cred; + int *a_ncookies; + u_int **cookies; + } */ *ap; +{ + register struct vnode *vp = ap->a_vp; + register struct ntnode *ip = VTONT(vp); + struct uio *uio = ap->a_uio; + struct ntfsmount *ntmp = ip->i_mp; + int i, error = 0; + u_int32_t faked = 0, num; + int ncookies = 0; + struct dirent cde; + off_t off; + + dprintf(("ntfs_readdir %d off: %d resid: %d\n",ip->i_number,(u_int32_t)uio->uio_offset,uio->uio_resid)); + + off = uio->uio_offset; + + /* Simulate . in every dir except ROOT */ + if( ip->i_number != NTFS_ROOTINO ) { + struct dirent dot = { NTFS_ROOTINO, + sizeof(struct dirent), DT_DIR, 1, "." }; + + if( uio->uio_offset < sizeof(struct dirent) ) { + dot.d_fileno = ip->i_number; + error = uiomove((char *)&dot,sizeof(struct dirent),uio); + if(error) + return (error); + + ncookies ++; + } + } + + /* Simulate .. in every dir including ROOT */ + if( uio->uio_offset < 2 * sizeof(struct dirent) ) { + struct dirent dotdot = { NTFS_ROOTINO, + sizeof(struct dirent), DT_DIR, 2, ".." }; + + error = uiomove((char *)&dotdot,sizeof(struct dirent),uio); + if(error) + return (error); + + ncookies ++; + } + + faked = (ip->i_number == NTFS_ROOTINO) ? 1 : 2; + num = uio->uio_offset / sizeof(struct dirent) - faked; + + while( uio->uio_resid >= sizeof(struct dirent) ) { + struct attr_indexentry *iep; + + error = ntfs_ntreaddir(ntmp, ip, num, &iep); + + if(error) + return (error); + + if( NULL == iep ) + break; + + while( !(iep->ie_flag & NTFS_IEFLAG_LAST) && (uio->uio_resid >= sizeof(struct dirent)) ) { + + if( ntfs_isnamepermitted(ntmp,iep) ) { + dprintf(("ntfs_readdir: elem: %d, fname:[",num)); + for(i=0;iie_fnamelen;i++) { + cde.d_name[i] = (char)iep->ie_fname[i]; + dprintf(("%c", cde.d_name[i])); + } + dprintf(("] type: %d, flag: %d, ",iep->ie_fnametype, iep->ie_flag)); + cde.d_name[i] = '\0'; + cde.d_namlen = iep->ie_fnamelen; + cde.d_fileno = iep->ie_number; + cde.d_type = (iep->ie_fflag & NTFS_FFLAG_DIR) ? DT_DIR : DT_REG; + cde.d_reclen = sizeof(struct dirent); + dprintf(("%s\n", (cde.d_type == DT_DIR) ? "dir":"reg")); + + error = uiomove((char *)&cde, sizeof(struct dirent), uio); + if(error) + return (error); + + ncookies++; + num++; + } + + iep = NTFS_NEXTREC(iep,struct attr_indexentry *); + } + } + + dprintf(("ntfs_readdir: %d entries (%d bytes) read\n", + ncookies,(u_int)(uio->uio_offset - off))); + dprintf(("ntfs_readdir: off: %d resid: %d\n", + (u_int32_t)uio->uio_offset,uio->uio_resid)); + + if (!error && ap->a_ncookies != NULL) { + struct dirent* dpStart; + struct dirent* dp; +#if __FreeBSD_version >= 300000 + u_long *cookies; + u_long *cookiep; +#else + u_int *cookies; + u_int *cookiep; +#endif + + printf("ntfs_readdir: %d cookies\n",ncookies); + if (uio->uio_segflg != UIO_SYSSPACE || uio->uio_iovcnt != 1) + panic("ntfs_readdir: unexpected uio from NFS server"); + dpStart = (struct dirent *) + (uio->uio_iov->iov_base - (uio->uio_offset - off)); +#if __FreeBSD_version >= 300000 + MALLOC(cookies, u_long *, ncookies * sizeof(u_long), + M_TEMP, M_WAITOK); +#else + MALLOC(cookies, u_int *, ncookies * sizeof(u_int), + M_TEMP, M_WAITOK); +#endif + for (dp = dpStart, cookiep = cookies, i=0; + i < ncookies; + dp = (struct dirent *)((caddr_t) dp + dp->d_reclen), i++) { + off += dp->d_reclen; + *cookiep++ = (u_int) off; + } + *ap->a_ncookies = ncookies; + *ap->a_cookies = cookies; + } +/* + if (ap->a_eofflag) + *ap->a_eofflag = VTONT(ap->a_vp)->i_size <= uio->uio_offset; +*/ + return (error); +} +/* +#undef dprintf +#define dprintf(a) +*/ + +int +ntfs_lookup(ap) + struct vop_lookup_args /* { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + } */ *ap; +{ + register struct vnode *dvp = ap->a_dvp; + register struct ntnode *dip = VTONT(dvp); + struct ntfsmount *ntmp = dip->i_mp; + struct componentname *cnp = ap->a_cnp; + struct ucred *cred = cnp->cn_cred; + int error; + int lockparent = cnp->cn_flags & LOCKPARENT; +#if NTFS_DEBUG + int wantparent = cnp->cn_flags & (LOCKPARENT|WANTPARENT); +#endif + dprintf(("ntfs_lookup: %s (%ld bytes) in %d, lp: %d, wp: %d \n", + cnp->cn_nameptr, cnp->cn_namelen, + dip->i_number,lockparent, wantparent)); + + error = VOP_ACCESS(dvp,VEXEC, cred, cnp->cn_proc); + if(error) + return (error); + + if( (cnp->cn_namelen == 1) && + !strncmp(cnp->cn_nameptr,".",1) ) { + dprintf(("ntfs_lookup: faking . directory in %d\n", + dip->i_number)); + + VREF(dvp); + *ap->a_vpp = dvp; + return (0); + } else if( (cnp->cn_namelen == 2) && + !strncmp(cnp->cn_nameptr,"..",2) && + (cnp->cn_flags & ISDOTDOT) ) { + struct ntvattr *vap; + + dprintf(("ntfs_lookup: faking .. directory in %d\n", + dip->i_number)); + + error = ntfs_ntvattrget(ntmp, dip, NTFS_A_NAME, NULL, 0, &vap); + if(error) + return (error); + +#if __FreeBSD_version >= 300000 + VOP_UNLOCK(dvp,0,cnp->cn_proc); +#else + VOP_UNLOCK(dvp); +#endif + + dprintf(("ntfs_lookup: parentdir: %d\n", + vap->va_a_name->n_pnumber)); + error = VFS_VGET(ntmp->ntm_mountp, + vap->va_a_name->n_pnumber,ap->a_vpp); + ntfs_ntvattrrele(vap); + if(error) { +#if __FreeBSD_version >= 300000 + VOP_LOCK(dvp, 0, cnp->cn_proc); +#else + VOP_LOCK(dvp); +#endif + return(error); + } + + if( lockparent && (cnp->cn_flags & ISLASTCN) && +#if __FreeBSD_version >= 300000 + (error = VOP_LOCK(dvp, 0, cnp->cn_proc)) ) { +#else + (error = VOP_LOCK(dvp)) ) { +#endif + vput( *(ap->a_vpp) ); + return (error); + } + return (error); + } else { + struct ntnode * nip; + + error = ntfs_ntlookup(ntmp, dip, cnp, &nip); + if(error) + return (error); + + dprintf(("ntfs_lookup: found ino: %d\n", nip->i_number)); + if( nip->i_number == dip->i_number ) { + ntfs_ntrele(nip); + VREF(dvp); + *ap->a_vpp = dvp; + return (0); + } + + *ap->a_vpp = ntfs_ihashget(ntmp->ntm_dev, nip->i_number); + if(*ap->a_vpp == NULL) { + error = getnewvnode(VT_NTFS, ntmp->ntm_mountp, + ntfs_vnodeop_p, ap->a_vpp); + if(error) { + ntfs_ntrele(nip); + return (error); + } + nip->i_vnode = *(ap->a_vpp); + (*ap->a_vpp)->v_data = nip; + (*ap->a_vpp)->v_type = nip->i_type; + + ntfs_ihashins(nip); + + VREF(nip->i_devvp); + } else { + printf("found in cache\n"); + ntfs_ntrele(nip); + } + + if(!lockparent || !(cnp->cn_flags & ISLASTCN)) +#if __FreeBSD_version >= 300000 + VOP_UNLOCK(dvp, 0, cnp->cn_proc); +#else + VOP_UNLOCK(dvp); +#endif + + } + return (error); +} + +/* + * Flush the blocks of a file to disk. + * + * This function is worthless for vnodes that represent directories. Maybe we + * could just do a sync if they try an fsync on a directory file. + */ +static int +ntfs_fsync(ap) + struct vop_fsync_args /* { + struct vnode *a_vp; + struct ucred *a_cred; + int a_waitfor; + struct proc *a_p; + } */ *ap; +{ + return (0); +} + +/* + * Global vfs data structures + */ +vop_t **ntfs_vnodeop_p; +static struct vnodeopv_entry_desc ntfs_vnodeop_entries[] = { + { &vop_default_desc, (vop_t *)ntfs_bypass }, + + { &vop_getattr_desc, (vop_t *)ntfs_getattr }, + { &vop_inactive_desc, (vop_t *)ntfs_inactive }, + { &vop_reclaim_desc, (vop_t *)ntfs_reclaim }, + { &vop_print_desc, (vop_t *)ntfs_print }, + +#if __FreeBSD_version >= 30000 + { &vop_islocked_desc, (vop_t *)vop_stdislocked }, + { &vop_unlock_desc, (vop_t *)vop_stdunlock }, + { &vop_lock_desc, (vop_t *)vop_stdlock }, + { &vop_cachedlookup_desc, (vop_t *)ntfs_lookup }, + { &vop_lookup_desc, (vop_t *)vfs_cache_lookup }, +#else + { &vop_islocked_desc, (vop_t *)ntfs_islocked }, + { &vop_unlock_desc, (vop_t *)ntfs_unlock }, + { &vop_lock_desc, (vop_t *)ntfs_lock }, + { &vop_lookup_desc, (vop_t *)ntfs_lookup }, +#endif + + { &vop_access_desc, (vop_t *)ntfs_access }, + { &vop_close_desc, (vop_t *)ntfs_close }, + { &vop_open_desc, (vop_t *)ntfs_open }, + { &vop_readdir_desc, (vop_t *)ntfs_readdir }, + { &vop_fsync_desc, (vop_t *)ntfs_fsync }, + + { &vop_bmap_desc, (vop_t *)ntfs_bmap }, + { &vop_getpages_desc, (vop_t *)ntfs_getpages }, + + { &vop_strategy_desc, (vop_t *)ntfs_strategy }, + { &vop_bwrite_desc, (vop_t *)ntfs_bwrite }, + { &vop_read_desc, (vop_t *)ntfs_read }, + + { NULL, NULL } +}; + +static struct vnodeopv_desc ntfs_vnodeop_opv_desc = + { &ntfs_vnodeop_p, ntfs_vnodeop_entries }; + +VNODEOP_SET(ntfs_vnodeop_opv_desc); diff --git a/sys/ntfs/ntfsmount.h b/sys/ntfs/ntfsmount.h new file mode 100644 index 0000000..9d68692 --- /dev/null +++ b/sys/ntfs/ntfsmount.h @@ -0,0 +1,39 @@ +/*- + * Copyright (c) 1998, 1999 Semen Ustimenko + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: ntfsmount.h,v 1.7 1999/01/28 03:56:07 semen Exp $ + */ + +#define NTFS_MFLAG_CASEINS 0x00000001 +#define NTFS_MFLAG_ALLNAMES 0x00000002 + +struct ntfs_args { + char *fspec; /* block special device to mount */ + struct export_args export; /* network export information */ + uid_t uid; /* uid that owns ntfs files */ + gid_t gid; /* gid that owns ntfs files */ + mode_t mode; /* mask to be applied for ntfs perms */ + u_long flag; /* additional flags */ +}; -- cgit v1.1