diff options
Diffstat (limited to 'sys/ntfs')
-rw-r--r-- | sys/ntfs/TODO | 38 | ||||
-rw-r--r-- | sys/ntfs/ntfs.h | 344 | ||||
-rw-r--r-- | sys/ntfs/ntfs_compr.c | 131 | ||||
-rw-r--r-- | sys/ntfs/ntfs_compr.h | 36 | ||||
-rw-r--r-- | sys/ntfs/ntfs_conv.c | 186 | ||||
-rw-r--r-- | sys/ntfs/ntfs_ihash.c | 185 | ||||
-rw-r--r-- | sys/ntfs/ntfs_ihash.h | 39 | ||||
-rw-r--r-- | sys/ntfs/ntfs_inode.h | 131 | ||||
-rw-r--r-- | sys/ntfs/ntfs_subr.c | 2187 | ||||
-rw-r--r-- | sys/ntfs/ntfs_subr.h | 145 | ||||
-rw-r--r-- | sys/ntfs/ntfs_vfsops.c | 1184 | ||||
-rw-r--r-- | sys/ntfs/ntfs_vfsops.h | 40 | ||||
-rw-r--r-- | sys/ntfs/ntfs_vnops.c | 1074 | ||||
-rw-r--r-- | sys/ntfs/ntfsmount.h | 36 |
14 files changed, 5756 insertions, 0 deletions
diff --git a/sys/ntfs/TODO b/sys/ntfs/TODO new file mode 100644 index 00000000000..18f6e9430d9 --- /dev/null +++ b/sys/ntfs/TODO @@ -0,0 +1,38 @@ +$NetBSD: TODO,v 1.1 2002/12/23 17:38:31 jdolecek Exp $ + +- convert the code to do caching in buffer cache indexed by + file-vnode/file-offset so that NTFS would take advantage of UBC; + it should also improve performance somewhat, because if it's necessary + to find some offset in a file, it's possible to check if the data + are available in the cache before doing the bmap operation first + (from Chuq) + +- working VOP_BALLOC() (new in UBC) + (from Chuq) + +- readdir: evalutate impact of using variable length reclen - should + enable us to store much more entries into buf in typical case with + short filenames, though more work would need to be done in offset != 0 + case + +- readdir: fill in proper "inode" number for '..' entry + +- handle Unicode filenames more inteligently - they are translated to UTF-2 + form currently, adding hooks for various character sets or other encodings + would be trivial + +- implement writing into resident attributes, so it would be possible + to write into "small" files (<30KB) too + (from Semen Ustimenko <semenu@FreeBSD.org>) + generally, better write support + + +- support mount update + +- really implement sync + +- write necessary bits to support NTFS in libsa + +- basic sysinst support for NTFS + +- NTFS ACL support (though usefullness of such feature is arguable) diff --git a/sys/ntfs/ntfs.h b/sys/ntfs/ntfs.h new file mode 100644 index 00000000000..9b72c3910ad --- /dev/null +++ b/sys/ntfs/ntfs.h @@ -0,0 +1,344 @@ +/* $Id: ntfs.h,v 1.1 2003/05/20 03:03:27 tedu Exp $ */ +/* $NetBSD: ntfs.h,v 1.5 2003/04/24 07:50:19 christos Exp $ */ + +/*- + * 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.5 1999/05/12 09:42:51 semenu Exp + */ + +/*#define NTFS_DEBUG 1*/ +#if defined(__NetBSD__) && defined(_KERNEL_OPT) +#include "opt_ntfs.h" +#endif + +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) +#define NTFS_FRFLAG_DIR 0x0002 +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_ATTRNAME_MAXLEN 0x40 +#define NTFS_ADFLAG_NONRES 0x0080 /* Attrib can be non resident */ +#define NTFS_ADFLAG_INDEX 0x0002 /* Attrib can be indexed */ +struct attrdef { + wchar ad_name[NTFS_ATTRNAME_MAXLEN]; + 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; +}; + +#define NTFS_BBID "NTFS " +#define NTFS_BBIDLEN 8 +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. */ +}; + +#pragma pack() + +typedef wchar (ntfs_wget_func_t) __P((const char **)); +typedef int (ntfs_wput_func_t) __P((char *, size_t, wchar)); +typedef int (ntfs_wcmp_func_t) __P((wchar, wchar)); + +#define NTFS_SYSNODESNUM 0x0B +struct ntfsmount { + struct mount *ntm_mountp; /* filesystem vfs structure */ + struct bootfile ntm_bootfile; + dev_t ntm_dev; /* device mounted */ + struct vnode *ntm_devvp; /* block device mounted vnode */ + struct vnode *ntm_sysvn[NTFS_SYSNODESNUM]; + u_int32_t ntm_bpmftrec; + uid_t ntm_uid; + gid_t ntm_gid; + mode_t ntm_mode; + u_long ntm_flag; + cn_t ntm_cfree; + struct ntvattrdef *ntm_ad; + int ntm_adnum; + struct netexport ntm_export; /* export information */ + ntfs_wget_func_t *ntm_wget; /* decode string to Unicode string */ + ntfs_wput_func_t *ntm_wput; /* encode Unicode string to string */ + ntfs_wcmp_func_t *ntm_wcmp; /* compare to wide characters */ +}; + +#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 + +#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) FTONT(VTOF(v)) +#define VTOF(v) ((struct fnode *)((v)->v_data)) +#define FTOV(f) ((f)->f_vp) +#define FTONT(f) ((f)->f_ip) +#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 || defined(__NetBSD__) +MALLOC_DECLARE(M_NTFSMNT); +MALLOC_DECLARE(M_NTFSNTNODE); +MALLOC_DECLARE(M_NTFSFNODE); +MALLOC_DECLARE(M_NTFSDIR); +MALLOC_DECLARE(M_NTFSNTHASH); +MALLOC_DECLARE(M_NTFSNTVATTR); +MALLOC_DECLARE(M_NTFSRDATA); +MALLOC_DECLARE(M_NTFSDECOMP); +MALLOC_DECLARE(M_NTFSRUN); +#endif + +#ifdef __NetBSD__ +typedef int (vop_t) __P((void *)); +#define HASHINIT(a, b, c, d) hashinit((a), HASH_LIST, (b), (c), (d)) +#define bqrelse(bp) brelse(bp) +#define VOP__UNLOCK(a, b, c) VOP_UNLOCK((a), (b)) +#define VGET(a, b, c) vget((a), (b)) +#define VN_LOCK(a, b, c) vn_lock((a), (b)) + +#elif defined(__OpenBSD__) +typedef int (vop_t) (void *); +#define HASHINIT(a, b, c, d) hashinit((a), (b), (c), (d)) +#define bqrelse(bp) brelse(bp) +#define VOP__UNLOCK(a, b, c) VOP_UNLOCK((a), (b), (c)) +#define VGET(a, b, c) vget((a), (b), (c)) +#define VN_LOCK(a, b, c) vn_lock((a), (b), (c)) + +#else /* !NetBSD && !OpenBSD */ +#define HASHINIT(a, b, c, d) hashinit((a), (b), (d)) +#define VOP__UNLOCK(a, b, c) VOP_UNLOCK((a), (b), (c)) +#define VGET(a, b, c) vget((a), (b), (c)) +#define VN_LOCK(a, b, c) vn_lock((a), (b), (c)) + +/* PDIRUNLOCK is used by NetBSD to mark if vfs_lookup() unlocked parent dir; + * on FreeBSD, it's not defined and nothing similar exists */ +#define PDIRUNLOCK 0 +#endif /* NetBSD */ + +#if defined(NTFS_DEBUG) +extern int ntfs_debug; +#define DPRINTF(X, Y) do { if(ntfs_debug >= (X)) printf Y; } while(0) +#define dprintf(a) DPRINTF(1, a) +#define ddprintf(a) DPRINTF(2, a) +#else /* NTFS_DEBUG */ +#define DPRINTF(X, Y) +#define dprintf(a) +#define ddprintf(a) +#endif + +extern vop_t **ntfs_vnodeop_p; diff --git a/sys/ntfs/ntfs_compr.c b/sys/ntfs/ntfs_compr.c new file mode 100644 index 00000000000..1fdec7739e2 --- /dev/null +++ b/sys/ntfs/ntfs_compr.c @@ -0,0 +1,131 @@ +/* $Id: ntfs_compr.c,v 1.1 2003/05/20 03:03:27 tedu Exp $ */ +/* $NetBSD: ntfs_compr.c,v 1.1 2002/12/23 17:38:31 jdolecek Exp $ */ + +/*- + * 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/05/12 09:42:54 semenu Exp + */ + +#include <sys/cdefs.h> +#ifdef __KERNEL_RCSID +__KERNEL_RCSID(0, "$NetBSD: ntfs_compr.c,v 1.1 2002/12/23 17:38:31 jdolecek Exp $"); +#endif + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/namei.h> +#include <sys/proc.h> +#include <sys/kernel.h> +#include <sys/vnode.h> +#include <sys/mount.h> +#include <sys/buf.h> +#include <sys/file.h> +#include <sys/malloc.h> +#ifdef __FreeBSD__ +#include <machine/clock.h> +#endif + +#include <miscfs/specfs/specdev.h> + +#if defined(__FreeBSD__) || defined(__NetBSD__) +#include <fs/ntfs/ntfs.h> +#include <fs/ntfs/ntfs_compr.h> +#else +#include <ntfs/ntfs.h> +#include <ntfs/ntfs_compr.h> +#endif + +#define GET_UINT16(addr) (*((u_int16_t *)(addr))) + +int +ntfs_uncompblock( + u_int8_t * buf, + 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, + 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 00000000000..3e31e3a13a8 --- /dev/null +++ b/sys/ntfs/ntfs_compr.h @@ -0,0 +1,36 @@ +/* $Id: ntfs_compr.h,v 1.1 2003/05/20 03:03:27 tedu Exp $ */ +/* $NetBSD: ntfs_compr.h,v 1.1 2002/12/23 17:38:32 jdolecek Exp $ */ + +/*- + * 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.4 1999/05/12 09:42:55 semenu Exp + */ + +#define NTFS_COMPBLOCK_SIZE 0x1000 +#define NTFS_COMPUNIT_CL 16 + +int ntfs_uncompblock(u_int8_t *, u_int8_t *); +int ntfs_uncompunit(struct ntfsmount *, u_int8_t *, u_int8_t *); diff --git a/sys/ntfs/ntfs_conv.c b/sys/ntfs/ntfs_conv.c new file mode 100644 index 00000000000..9652e7af5c1 --- /dev/null +++ b/sys/ntfs/ntfs_conv.c @@ -0,0 +1,186 @@ +/* $Id: ntfs_conv.c,v 1.1 2003/05/20 03:03:27 tedu Exp $ */ +/* $NetBSD: ntfs_conv.c,v 1.1 2002/12/23 17:38:32 jdolecek Exp $ */ + +/*- + * Copyright (c) 2001 The NetBSD Foundation, Inc. + * 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 NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +/* + * File name recode stuff. + * + * The utf-8 routines were derived from basesrc/lib/libc/locale/utf2.c. + */ + +#include <sys/cdefs.h> +#ifdef __KERNEL_RCSID +__KERNEL_RCSID(0, "$NetBSD: ntfs_conv.c,v 1.1 2002/12/23 17:38:32 jdolecek Exp $"); +#endif + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/namei.h> +#include <sys/proc.h> +#include <sys/kernel.h> +#include <sys/vnode.h> +#include <sys/mount.h> +#include <sys/buf.h> +#include <sys/file.h> +#include <sys/malloc.h> +#include <sys/lock.h> +#if defined(__FreeBSD__) +#include <machine/clock.h> +#endif + +#include <miscfs/specfs/specdev.h> + +/* #define NTFS_DEBUG 1 */ +#if defined(__FreeBSD__) || defined(__NetBSD__) +#include <fs/ntfs/ntfs.h> +#include <fs/ntfs/ntfsmount.h> +#include <fs/ntfs/ntfs_inode.h> +#include <fs/ntfs/ntfs_vfsops.h> +#include <fs/ntfs/ntfs_subr.h> +#include <fs/ntfs/ntfs_compr.h> +#include <fs/ntfs/ntfs_ihash.h> +#else +#include <ntfs/ntfs.h> +#include <ntfs/ntfsmount.h> +#include <ntfs/ntfs_inode.h> +#include <ntfs/ntfs_vfsops.h> +#include <ntfs/ntfs_subr.h> +#include <ntfs/ntfs_compr.h> +#include <ntfs/ntfs_ihash.h> +#endif + +/* UTF-8 encoding stuff */ + +static const int _utf_count[16] = { + 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 2, 2, 3, 0, +}; + +/* + * Read one wide character off the string, shift the string pointer + * and return the character. + */ +wchar +ntfs_utf8_wget(const char **str) +{ + int c; + wchar rune = 0; + const char *s = *str; + + c = _utf_count[(s[0] >> 4) & 0xf]; + if (c == 0) { + c = 1; + goto encoding_error; + } + + switch (c) { + case 1: + rune = s[0] & 0xff; + break; + case 2: + if ((s[1] & 0xc0) != 0x80) + goto encoding_error; + rune = ((s[0] & 0x1F) << 6) | (s[1] & 0x3F); + break; + case 3: + if ((s[1] & 0xC0) != 0x80 || (s[2] & 0xC0) != 0x80) + goto encoding_error; + rune = ((s[0] & 0x1F) << 12) | ((s[1] & 0x3F) << 6) + | (s[2] & 0x3F); + break; + } + +encoding_error: + *str = *str + c; + return rune; +} + +/* + * Encode wide character and write it to the string. 'n' specifies + * how much space there is in the string. Returns number of bytes written + * to the target string. + */ +int +ntfs_utf8_wput(char *s, size_t n, wchar wc) +{ + if (wc & 0xf800) { + if (n < 3) { + /* bound check failure */ + ddprintf(("ntfs_utf8_wput: need 3 bytes\n")); + return 0; + } + + s[0] = 0xE0 | ((wc >> 12) & 0x0F); + s[1] = 0x80 | ((wc >> 6) & 0x3F); + s[2] = 0x80 | ((wc) & 0x3F); + return 3; + } else { + if (wc & 0x0780) { + if (n < 2) { + /* bound check failure */ + ddprintf(("ntfs_utf8_wput: need 2 bytes\n")); + return 0; + } + + s[0] = 0xC0 | ((wc >> 6) & 0x1F); + s[1] = 0x80 | ((wc) & 0x3F); + return 2; + } else { + if (n < 1) { + /* bound check failure */ + ddprintf(("ntfs_utf8_wput: need 1 byte\n")); + return 0; + } + + s[0] = wc; + return 1; + } + } +} + +/* + * Compare two wide characters, returning 1, 0, -1 if the first is + * bigger, equal or lower than the second. + */ +int +ntfs_utf8_wcmp(wchar wc1, wchar wc2) +{ + /* no conversions needed for utf8 */ + + if (wc1 == wc2) + return 0; + else + return (int) wc1 - (int) wc2; +} diff --git a/sys/ntfs/ntfs_ihash.c b/sys/ntfs/ntfs_ihash.c new file mode 100644 index 00000000000..a189f35a5e3 --- /dev/null +++ b/sys/ntfs/ntfs_ihash.c @@ -0,0 +1,185 @@ +/* $Id: ntfs_ihash.c,v 1.1 2003/05/20 03:03:27 tedu Exp $ */ +/* $NetBSD: ntfs_ihash.c,v 1.1 2002/12/23 17:38:32 jdolecek Exp $ */ + +/* + * 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.5 1999/05/12 09:42:58 semenu Exp + */ + +#include <sys/cdefs.h> +#ifdef __KERNEL_RCSID +__KERNEL_RCSID(0, "$NetBSD: ntfs_ihash.c,v 1.1 2002/12/23 17:38:32 jdolecek Exp $"); +#endif + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/vnode.h> +#include <sys/malloc.h> +#include <sys/proc.h> +#include <sys/mount.h> + +#if defined(__FreeBSD__) || defined(__NetBSD__) +#include <fs/ntfs/ntfs.h> +#include <fs/ntfs/ntfs_inode.h> +#include <fs/ntfs/ntfs_ihash.h> +#else +#include <ntfs/ntfs.h> +#include <ntfs/ntfs_inode.h> +#include <ntfs/ntfs_ihash.h> +#endif + +#ifdef MALLOC_DEFINE +MALLOC_DEFINE(M_NTFSNTHASH, "NTFS nthash", "NTFS ntnode hash tables"); +#endif + +/* + * Structures associated with inode cacheing. + */ +static LIST_HEAD(nthashhead, ntnode) *ntfs_nthashtbl; +static u_long ntfs_nthash; /* size of hash table - 1 */ +#define NTNOHASH(device, inum) ((minor(device) + (inum)) & ntfs_nthash) +#ifndef NULL_SIMPLELOCKS +static struct simplelock ntfs_nthash_slock; +#endif +struct lock ntfs_hashlock; + +/* + * Initialize inode hash table. + */ +void +ntfs_nthashinit() +{ + lockinit(&ntfs_hashlock, PINOD, "ntfs_nthashlock", 0, 0); + ntfs_nthashtbl = HASHINIT(desiredvnodes, M_NTFSNTHASH, M_WAITOK, + &ntfs_nthash); + simple_lock_init(&ntfs_nthash_slock); +} + +#ifdef __NetBSD__ +/* + * Reinitialize inode hash table. + */ + +void +ntfs_nthashreinit() +{ + struct ntnode *ip; + struct nthashhead *oldhash, *hash; + u_long oldmask, mask, val; + int i; + + hash = HASHINIT(desiredvnodes, M_NTFSNTHASH, M_WAITOK, &mask); + + simple_lock(&ntfs_nthash_slock); + oldhash = ntfs_nthashtbl; + oldmask = ntfs_nthash; + ntfs_nthashtbl = hash; + ntfs_nthash = mask; + for (i = 0; i <= oldmask; i++) { + while ((ip = LIST_FIRST(&oldhash[i])) != NULL) { + LIST_REMOVE(ip, i_hash); + val = NTNOHASH(ip->i_dev, ip->i_number); + LIST_INSERT_HEAD(&hash[val], ip, i_hash); + } + } + simple_unlock(&ntfs_nthash_slock); + hashdone(oldhash, M_NTFSNTHASH); +} + +/* + * Free the inode hash table. Called from ntfs_done(), only needed + * on NetBSD. + */ +void +ntfs_nthashdone() +{ + hashdone(ntfs_nthashtbl, M_NTFSNTHASH); +} +#endif + +/* + * 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 ntnode * +ntfs_nthashlookup(dev, inum) + dev_t dev; + ino_t inum; +{ + struct ntnode *ip; + struct nthashhead *ipp; + + simple_lock(&ntfs_nthash_slock); + ipp = &ntfs_nthashtbl[NTNOHASH(dev, inum)]; + LIST_FOREACH(ip, ipp, i_hash) { + if (inum == ip->i_number && dev == ip->i_dev) + break; + } + simple_unlock(&ntfs_nthash_slock); + + return (ip); +} + +/* + * Insert the ntnode into the hash table. + */ +void +ntfs_nthashins(ip) + struct ntnode *ip; +{ + struct nthashhead *ipp; + + simple_lock(&ntfs_nthash_slock); + ipp = &ntfs_nthashtbl[NTNOHASH(ip->i_dev, ip->i_number)]; + LIST_INSERT_HEAD(ipp, ip, i_hash); + ip->i_flag |= IN_HASHED; + simple_unlock(&ntfs_nthash_slock); +} + +/* + * Remove the inode from the hash table. + */ +void +ntfs_nthashrem(ip) + struct ntnode *ip; +{ + simple_lock(&ntfs_nthash_slock); + if (ip->i_flag & IN_HASHED) { + ip->i_flag &= ~IN_HASHED; + LIST_REMOVE(ip, i_hash); + } + simple_unlock(&ntfs_nthash_slock); +} diff --git a/sys/ntfs/ntfs_ihash.h b/sys/ntfs/ntfs_ihash.h new file mode 100644 index 00000000000..b16087f95db --- /dev/null +++ b/sys/ntfs/ntfs_ihash.h @@ -0,0 +1,39 @@ +/* $Id: ntfs_ihash.h,v 1.1 2003/05/20 03:03:27 tedu Exp $ */ +/* $NetBSD: ntfs_ihash.h,v 1.1 2002/12/23 17:38:32 jdolecek Exp $ */ + +/*- + * 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_ihash.h,v 1.3 1999/05/12 09:42:59 semenu Exp + */ + +extern struct lock ntfs_hashlock; +void ntfs_nthashinit __P((void)); +void ntfs_nthashreinit __P((void)); +void ntfs_nthashdone __P((void)); +struct ntnode *ntfs_nthashlookup __P((dev_t, ino_t)); +struct ntnode *ntfs_nthashget __P((dev_t, ino_t)); +void ntfs_nthashins __P((struct ntnode *)); +void ntfs_nthashrem __P((struct ntnode *)); diff --git a/sys/ntfs/ntfs_inode.h b/sys/ntfs/ntfs_inode.h new file mode 100644 index 00000000000..2d005e88cc6 --- /dev/null +++ b/sys/ntfs/ntfs_inode.h @@ -0,0 +1,131 @@ +/* $Id: ntfs_inode.h,v 1.1 2003/05/20 03:03:27 tedu Exp $ */ +/* $NetBSD: ntfs_inode.h,v 1.1 2002/12/23 17:38:33 jdolecek Exp $ */ + +/*- + * 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.4 1999/05/12 09:43:00 semenu Exp + */ + +#if defined(__FreeBSD__) || defined(__NetBSD__) +#include <miscfs/genfs/genfs_node.h> +#endif + +/* These flags are kept in i_flag. */ +#if defined(__FreeBSD__) +#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_LAZYMOD 0x0080 /* Modified, but don't write yet. */ +#else /* defined(__NetBSD__) */ +#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_HASHED 0x0800 /* Inode is on hash list */ +#define IN_LOADED 0x8000 /* ntvattrs loaded */ +#define IN_PRELOADED 0x4000 /* loaded from directory entry */ + +struct ntnode { + struct vnode *i_devvp; /* vnode of blk dev we live on */ + dev_t i_dev; /* Device associated with the inode. */ + + LIST_ENTRY(ntnode) i_hash; + struct ntnode *i_next; + struct ntnode **i_prev; + struct ntfsmount *i_mp; + ino_t i_number; + u_int32_t i_flag; + + /* locking */ + struct lock i_lock; + struct simplelock i_interlock; + int i_usecount; + + LIST_HEAD(,fnode) i_fnlist; + LIST_HEAD(,ntvattr) i_valist; + + long i_nlink; /* MFR */ + ino_t i_mainrec; /* MFR */ + u_int32_t i_frflag; /* MFR */ +}; + +#define FN_PRELOADED 0x0001 +#define FN_VALID 0x0002 +#define FN_AATTRNAME 0x0004 /* space allocated for f_attrname */ +struct fnode { +#ifdef __FreeBSD__ + struct lock f_lock; /* fnode lock >Keep this first< */ +#endif +#if defined(__FreeBSD__) || defined(__NetBSD__) + struct genfs_node f_gnode; +#endif + + LIST_ENTRY(fnode) f_fnlist; + struct vnode *f_vp; /* Associatied vnode */ + struct ntnode *f_ip; /* Associated ntnode */ + u_long f_flag; + + ntfs_times_t f_times; /* $NAME/dirinfo */ + ino_t f_pnumber; /* $NAME/dirinfo */ + u_int32_t f_fflag; /* $NAME/dirinfo */ + u_int64_t f_size; /* defattr/dirinfo: */ + u_int64_t f_allocated; /* defattr/dirinfo */ + + u_int32_t f_attrtype; + char *f_attrname; + + /* for ntreaddir */ + u_int32_t f_lastdattr; + u_int32_t f_lastdblnum; + u_int32_t f_lastdoff; + u_int32_t f_lastdnum; + caddr_t f_dirblbuf; + u_int32_t f_dirblsz; +}; + +/* This overlays the fid structure (see <sys/mount.h>) */ +struct ntfid { + u_int16_t ntfid_len; /* Length of structure. */ + u_int16_t ntfid_pad; /* Force 32-bit alignment. */ + ino_t ntfid_ino; /* File number (ino). */ + u_int8_t ntfid_attr; /* Attribute identifier */ +#ifdef notyet + int32_t ntfid_gen; /* Generation number. */ +#endif +}; diff --git a/sys/ntfs/ntfs_subr.c b/sys/ntfs/ntfs_subr.c new file mode 100644 index 00000000000..49aaa35b8b7 --- /dev/null +++ b/sys/ntfs/ntfs_subr.c @@ -0,0 +1,2187 @@ +/* $Id: ntfs_subr.c,v 1.1 2003/05/20 03:03:27 tedu Exp $ */ +/* $NetBSD: ntfs_subr.c,v 1.4 2003/04/10 21:37:32 jdolecek Exp $ */ + +/*- + * Copyright (c) 1998, 1999 Semen Ustimenko (semenu@FreeBSD.org) + * 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.4 1999/05/12 09:43:01 semenu Exp + */ + +#include <sys/cdefs.h> +#ifdef __KERNEL_RCSID +__KERNEL_RCSID(0, "$NetBSD: ntfs_subr.c,v 1.4 2003/04/10 21:37:32 jdolecek Exp $"); +#endif + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/namei.h> +#include <sys/proc.h> +#include <sys/kernel.h> +#include <sys/vnode.h> +#include <sys/mount.h> +#include <sys/buf.h> +#include <sys/file.h> +#include <sys/malloc.h> +#include <sys/lock.h> +#if defined(__FreeBSD__) +#include <machine/clock.h> +#endif + +#include <miscfs/specfs/specdev.h> + +/* #define NTFS_DEBUG 1 */ +#if defined(__FreeBSD__) || defined(__NetBSD__) +#include <fs/ntfs/ntfs.h> +#include <fs/ntfs/ntfsmount.h> +#include <fs/ntfs/ntfs_inode.h> +#include <fs/ntfs/ntfs_vfsops.h> +#include <fs/ntfs/ntfs_subr.h> +#include <fs/ntfs/ntfs_compr.h> +#include <fs/ntfs/ntfs_ihash.h> +#else +#include <ntfs/ntfs.h> +#include <ntfs/ntfsmount.h> +#include <ntfs/ntfs_inode.h> +#include <ntfs/ntfs_vfsops.h> +#include <ntfs/ntfs_subr.h> +#include <ntfs/ntfs_compr.h> +#include <ntfs/ntfs_ihash.h> +#endif + +#if defined(NTFS_DEBUG) +int ntfs_debug = NTFS_DEBUG; +#endif + +#ifdef MALLOC_DEFINE +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 + +/* Local struct used in ntfs_ntlookupfile() */ +struct ntfs_lookup_ctx { + u_int32_t aoff; + u_int32_t rdsize; + cn_t cn; + struct ntfs_lookup_ctx *prev; +}; + +static int ntfs_ntlookupattr __P((struct ntfsmount *, const char *, int, int *, char **)); +static int ntfs_findvattr __P((struct ntfsmount *, struct ntnode *, struct ntvattr **, struct ntvattr **, u_int32_t, const char *, size_t, cn_t)); +static int ntfs_uastricmp __P((struct ntfsmount *, const wchar *, size_t, const char *, size_t)); +static int ntfs_uastrcmp __P((struct ntfsmount *, const wchar *, size_t, const char *, size_t)); + +/* table for mapping Unicode chars into uppercase; it's filled upon first + * ntfs mount, freed upon last ntfs umount */ +static wchar *ntfs_toupper_tab; +#define NTFS_U28(ch) ((((ch) & 0xE0) == 0) ? '_' : (ch) & 0xFF) +#define NTFS_TOUPPER(ch) (ntfs_toupper_tab[(unsigned char)(ch)]) +static struct lock ntfs_toupper_lock; +static signed int ntfs_toupper_usecount; + +/* support macro for ntfs_ntvattrget() */ +#define NTFS_AALPCMP(aalp,type,name,namelen) ( \ + (aalp->al_type == type) && (aalp->al_namelen == namelen) && \ + !ntfs_uastrcmp(ntmp, aalp->al_name,aalp->al_namelen,name,namelen) ) + +/* + * + */ +int +ntfs_ntvattrrele(vap) + struct ntvattr * vap; +{ + dprintf(("ntfs_ntvattrrele: ino: %d, type: 0x%x\n", + vap->va_ip->i_number, vap->va_type)); + + ntfs_ntrele(vap->va_ip); + + return (0); +} + +/* + * find the attribute in the ntnode + */ +static int +ntfs_findvattr(ntmp, ip, lvapp, vapp, type, name, namelen, vcn) + struct ntfsmount *ntmp; + struct ntnode *ip; + struct ntvattr **lvapp, **vapp; + u_int32_t type; + const char *name; + size_t namelen; + cn_t vcn; +{ + int error; + struct ntvattr *vap; + + if((ip->i_flag & IN_LOADED) == 0) { + dprintf(("ntfs_findvattr: node not loaded, ino: %d\n", + ip->i_number)); + error = ntfs_loadntnode(ntmp,ip); + if (error) { + printf("ntfs_findvattr: FAILED TO LOAD INO: %d\n", + ip->i_number); + return (error); + } + } + + *lvapp = NULL; + *vapp = NULL; + for (vap = ip->i_valist.lh_first; vap; vap = vap->va_list.le_next) { + ddprintf(("ntfs_findvattr: 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) == 0)) { + *vapp = vap; + ntfs_ntref(vap->va_ip); + return (0); + } + if (vap->va_type == NTFS_A_ATTRLIST) + *lvapp = vap; + } + + return (-1); +} + +/* + * Search attribute specified in ntnode (load ntnode if necessary). + * If not found but ATTR_A_ATTRLIST present, read it in and search through. + * VOP_VGET node needed, and lookup through its ntnode (load if nessesary). + * + * ntnode should be locked + */ +int +ntfs_ntvattrget( + struct ntfsmount * ntmp, + struct ntnode * ip, + u_int32_t type, + const char *name, + cn_t vcn, + struct ntvattr ** vapp) +{ + struct ntvattr *lvap = NULL; + struct attr_attrlist *aalp; + struct attr_attrlist *nextaalp; + struct vnode *newvp; + struct ntnode *newip; + caddr_t alpool; + size_t namelen, len; + int error; + + *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; + } + + error = ntfs_findvattr(ntmp, ip, &lvap, vapp, type, name, namelen, vcn); + if (error >= 0) + return (error); + + 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; + alpool = (caddr_t) malloc(len, M_TEMP, M_WAITOK); + error = ntfs_readntvattr_plain(ntmp, ip, lvap, 0, len, alpool, &len, + NULL); + if (error) + goto out; + + aalp = (struct attr_attrlist *) alpool; + nextaalp = NULL; + + for(; len > 0; aalp = nextaalp) { + 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; + + if (!NTFS_AALPCMP(aalp, type, name, namelen) || + (nextaalp && (nextaalp->al_vcnstart <= vcn) && + NTFS_AALPCMP(nextaalp, type, name, namelen))) + continue; + + dprintf(("ntfs_ntvattrget: attribute in ino: %d\n", + aalp->al_inumber)); + + /* this is not a main record, so we can't use just plain + vget() */ + error = ntfs_vgetex(ntmp->ntm_mountp, aalp->al_inumber, + NTFS_A_DATA, NULL, LK_EXCLUSIVE, + VG_EXT, curproc, &newvp); + if (error) { + printf("ntfs_ntvattrget: CAN'T VGET INO: %d\n", + aalp->al_inumber); + goto out; + } + newip = VTONT(newvp); + /* XXX have to lock ntnode */ + error = ntfs_findvattr(ntmp, newip, &lvap, vapp, + type, name, namelen, vcn); + vput(newvp); + if (error == 0) + goto out; + printf("ntfs_ntvattrget: ATTRLIST ERROR.\n"); + break; + } + error = ENOENT; + + dprintf(("ntfs_ntvattrget: UNEXISTED ATTRIBUTE: " \ + "ino: %d, type: 0x%x, name: %.*s, vcn: %d\n", \ + ip->i_number, type, (int) namelen, name, (u_int32_t) vcn)); +out: + free(alpool, M_TEMP); + return (error); +} + +/* + * Read ntnode from disk, make ntvattr list. + * + * ntnode should be locked + */ +int +ntfs_loadntnode( + struct ntfsmount * ntmp, + struct ntnode * ip) +{ + struct filerec *mfrp; + daddr_t bn; + int error,off; + struct attr *ap; + struct ntvattr *nvap; + + dprintf(("ntfs_loadntnode: loading ino: %d\n",ip->i_number)); + + mfrp = (struct filerec *) malloc(ntfs_bntob(ntmp->ntm_bpmftrec), + M_TEMP, M_WAITOK); + + if (ip->i_number < NTFS_SYSNODESNUM) { + struct buf *bp; + + dprintf(("ntfs_loadntnode: 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_loadntnode: 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_readattr(ntmp, VTONT(vp), NTFS_A_DATA, NULL, + ip->i_number * ntfs_bntob(ntmp->ntm_bpmftrec), + ntfs_bntob(ntmp->ntm_bpmftrec), mfrp, NULL); + if (error) { + printf("ntfs_loadntnode: ntfs_readattr 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_loadntnode: BAD MFT RECORD %d\n", + (u_int32_t) ip->i_number); + goto out; + } + + dprintf(("ntfs_loadntnode: load attrs for ino: %d\n",ip->i_number)); + off = mfrp->fr_attroff; + ap = (struct attr *) ((caddr_t)mfrp + off); + + LIST_INIT(&ip->i_valist); + + while (ap->a_hdr.a_type != -1) { + error = ntfs_attrtontvattr(ntmp, &nvap, ap); + if (error) + break; + nvap->va_ip = ip; + + LIST_INSERT_HEAD(&ip->i_valist, nvap, va_list); + + off += ap->a_hdr.reclen; + ap = (struct attr *) ((caddr_t)mfrp + off); + } + if (error) { + printf("ntfs_loadntnode: 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; + +out: + free(mfrp, M_TEMP); + return (error); +} + +/* + * Routine locks ntnode and increase usecount, just opposite of + * ntfs_ntput(). + */ +int +ntfs_ntget( + struct ntnode *ip, +#ifdef __OpenBSD__ + struct proc *p +#endif + ) +{ + dprintf(("ntfs_ntget: get ntnode %d: %p, usecount: %d\n", + ip->i_number, ip, ip->i_usecount)); + + simple_lock(&ip->i_interlock); + ip->i_usecount++; +#ifndef __OpenBSD__ + lockmgr(&ip->i_lock, LK_EXCLUSIVE | LK_INTERLOCK, &ip->i_interlock); +#else + lockmgr(&ip->i_lock, LK_EXCLUSIVE | LK_INTERLOCK, &ip->i_interlock, p); +#endif + + return 0; +} + +/* + * Routine search ntnode in hash, if found: lock, inc usecount and return. + * If not in hash allocate structure for ntnode, prefill it, lock, + * inc count and return. + * + * ntnode returned locked + */ +int +ntfs_ntlookup( + struct ntfsmount * ntmp, + ino_t ino, +#ifndef __OpenBSD__ + struct ntnode ** ipp) +#else + struct ntnode ** ipp, + struct proc * p) +#endif +{ + struct ntnode *ip; + + dprintf(("ntfs_ntlookup: looking for ntnode %d\n", ino)); + + do { + if ((ip = ntfs_nthashlookup(ntmp->ntm_dev, ino)) != NULL) { +#ifndef __OpenBSD__ + ntfs_ntget(ip); +#else + ntfs_ntget(ip, p); +#endif + dprintf(("ntfs_ntlookup: ntnode %d: %p, usecount: %d\n", + ino, ip, ip->i_usecount)); + *ipp = ip; + return (0); + } +#ifndef __OpenBSD__ + } while (lockmgr(&ntfs_hashlock, LK_EXCLUSIVE | LK_SLEEPFAIL, NULL)); +#else + } while (lockmgr(&ntfs_hashlock, LK_EXCLUSIVE | LK_SLEEPFAIL, NULL, p)); +#endif + + MALLOC(ip, struct ntnode *, sizeof(struct ntnode), + M_NTFSNTNODE, M_WAITOK); + ddprintf(("ntfs_ntlookup: allocating ntnode: %d: %p\n", ino, ip)); + bzero((caddr_t) ip, sizeof(struct ntnode)); + + /* Generic initialization */ + ip->i_devvp = ntmp->ntm_devvp; + ip->i_dev = ntmp->ntm_dev; + ip->i_number = ino; + ip->i_mp = ntmp; + + LIST_INIT(&ip->i_fnlist); + + /* init lock and lock the newborn ntnode */ + lockinit(&ip->i_lock, PINOD, "ntnode", 0, LK_EXCLUSIVE); + simple_lock_init(&ip->i_interlock); +#ifndef __OpenBSD__ + ntfs_ntget(ip); +#else + ntfs_ntget(ip, p); +#endif + + ntfs_nthashins(ip); + +#ifndef __OpenBSD__ + lockmgr(&ntfs_hashlock, LK_RELEASE, NULL); +#else + lockmgr(&ntfs_hashlock, LK_RELEASE, NULL, p); +#endif + + *ipp = ip; + + dprintf(("ntfs_ntlookup: ntnode %d: %p, usecount: %d\n", + ino, ip, ip->i_usecount)); + + return (0); +} + +/* + * Decrement usecount of ntnode and unlock it, if usecount reach zero, + * deallocate ntnode. + * + * ntnode should be locked on entry, and unlocked on return. + */ +void +ntfs_ntput( + struct ntnode *ip, +#ifdef __OpenBSD__ + struct proc *p +#endif + ) +{ + struct ntvattr *vap; + + dprintf(("ntfs_ntput: rele ntnode %d: %p, usecount: %d\n", + ip->i_number, ip, ip->i_usecount)); + + simple_lock(&ip->i_interlock); + ip->i_usecount--; + +#ifdef DIAGNOSTIC + if (ip->i_usecount < 0) { + panic("ntfs_ntput: ino: %d usecount: %d ", + ip->i_number,ip->i_usecount); + } +#endif + +#ifndef __OpenBSD__ + lockmgr(&ip->i_lock, LK_RELEASE|LK_INTERLOCK, &ip->i_interlock); +#else + lockmgr(&ip->i_lock, LK_RELEASE|LK_INTERLOCK, &ip->i_interlock, p); +#endif + + if (ip->i_usecount == 0) { + dprintf(("ntfs_ntput: deallocating ntnode: %d\n", + ip->i_number)); + + if (ip->i_fnlist.lh_first) + panic("ntfs_ntput: ntnode has fnodes"); + + ntfs_nthashrem(ip); + + while (ip->i_valist.lh_first != NULL) { + vap = ip->i_valist.lh_first; + LIST_REMOVE(vap,va_list); + ntfs_freentvattr(vap); + } + FREE(ip, M_NTFSNTNODE); + } +} + +/* + * increment usecount of ntnode + */ +void +ntfs_ntref(ip) + struct ntnode *ip; +{ + simple_lock(&ip->i_interlock); + ip->i_usecount++; + simple_unlock(&ip->i_interlock); + + dprintf(("ntfs_ntref: ino %d, usecount: %d\n", + ip->i_number, ip->i_usecount)); + +} + +/* + * Decrement usecount of ntnode. + */ +void +ntfs_ntrele(ip) + struct ntnode *ip; +{ + dprintf(("ntfs_ntrele: rele ntnode %d: %p, usecount: %d\n", + ip->i_number, ip, ip->i_usecount)); + + simple_lock(&ip->i_interlock); + ip->i_usecount--; + + if (ip->i_usecount < 0) + panic("ntfs_ntrele: ino: %d usecount: %d ", + ip->i_number,ip->i_usecount); + simple_unlock(&ip->i_interlock); +} + +/* + * Deallocate all memory allocated for ntvattr + */ +void +ntfs_freentvattr(vap) + 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); +} + +/* + * Convert disk image of attribute into ntvattr structure, + * runs are expanded also. + */ +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(struct ntvattr), + M_NTFSNTVATTR, M_WAITOK); + bzero(vap, sizeof(struct ntvattr)); + 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_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); + vap->va_datap = (caddr_t) malloc(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); +} + +/* + * Expand run into more utilizable and more memory eating format. + */ +int +ntfs_runtovrun( + cn_t ** rcnp, + cn_t ** rclp, + u_long * rcntp, + u_int8_t * run) +{ + u_int32_t off; + u_int32_t sz, i; + cn_t *cn; + cn_t *cl; + u_long cnt; + cn_t prev; + cn_t tmp; + + off = 0; + cnt = 0; + i = 0; + while (run[off]) { + off += (run[off] & 0xF) + ((run[off] >> 4) & 0xF) + 1; + cnt++; + } + cn = (cn_t *) malloc(cnt * sizeof(cn_t), M_NTFSRUN, M_WAITOK); + cl = (cn_t *) malloc(cnt * sizeof(cn_t), M_NTFSRUN, M_WAITOK); + + off = 0; + cnt = 0; + prev = 0; + while (run[off]) { + + 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); +} + +/* + * Compare unicode and ascii string case insens. + */ +static int +ntfs_uastricmp(ntmp, ustr, ustrlen, astr, astrlen) + struct ntfsmount *ntmp; + const wchar *ustr; + size_t ustrlen; + const char *astr; + size_t astrlen; +{ + size_t i; + int res; + const char *astrend = astr + astrlen; + + for (i = 0; i < ustrlen && astr < astrend; i++) { + res = (*ntmp->ntm_wcmp)(NTFS_TOUPPER(ustr[i]), + NTFS_TOUPPER((*ntmp->ntm_wget)(&astr)) ); + if (res) + return res; + } + + if (i == ustrlen && astr == astrend) + return 0; + else if (i == ustrlen) + return -1; + else + return 1; +} + +/* + * Compare unicode and ascii string case sens. + */ +static int +ntfs_uastrcmp(ntmp, ustr, ustrlen, astr, astrlen) + struct ntfsmount *ntmp; + const wchar *ustr; + size_t ustrlen; + const char *astr; + size_t astrlen; +{ + size_t i; + int res; + const char *astrend = astr + astrlen; + + for (i = 0; (i < ustrlen) && (astr < astrend); i++) { + res = (*ntmp->ntm_wcmp)(ustr[i], (*ntmp->ntm_wget)(&astr)); + if (res) + return res; + } + + if (i == ustrlen && astr == astrend) + return 0; + else if (i == ustrlen) + return -1; + else + return 1; +} + +/* + * Search fnode in ntnode, if not found allocate and preinitialize. + * + * ntnode should be locked on entry. + */ +int +ntfs_fget( + struct ntfsmount *ntmp, + struct ntnode *ip, + int attrtype, + char *attrname, + struct fnode **fpp) +{ + struct fnode *fp; + + dprintf(("ntfs_fget: ino: %d, attrtype: 0x%x, attrname: %s\n", + ip->i_number,attrtype, attrname?attrname:"")); + *fpp = NULL; + for (fp = ip->i_fnlist.lh_first; fp != NULL; fp = fp->f_fnlist.le_next){ + dprintf(("ntfs_fget: fnode: attrtype: %d, attrname: %s\n", + fp->f_attrtype, fp->f_attrname?fp->f_attrname:"")); + + if ((attrtype == fp->f_attrtype) && + ((!attrname && !fp->f_attrname) || + (attrname && fp->f_attrname && + !strcmp(attrname,fp->f_attrname)))){ + dprintf(("ntfs_fget: found existed: %p\n",fp)); + *fpp = fp; + } + } + + if (*fpp) + return (0); + + MALLOC(fp, struct fnode *, sizeof(struct fnode), M_NTFSFNODE, M_WAITOK); + bzero(fp, sizeof(struct fnode)); + dprintf(("ntfs_fget: allocating fnode: %p\n",fp)); + + fp->f_ip = ip; + fp->f_attrname = attrname; + if (fp->f_attrname) fp->f_flag |= FN_AATTRNAME; + fp->f_attrtype = attrtype; + + ntfs_ntref(ip); + + LIST_INSERT_HEAD(&ip->i_fnlist, fp, f_fnlist); + + *fpp = fp; + + return (0); +} + +/* + * Deallocate fnode, remove it from ntnode's fnode list. + * + * ntnode should be locked. + */ +void +ntfs_frele( + struct fnode *fp) +{ + struct ntnode *ip = FTONT(fp); + + dprintf(("ntfs_frele: fnode: %p for %d: %p\n", fp, ip->i_number, ip)); + + dprintf(("ntfs_frele: deallocating fnode\n")); + LIST_REMOVE(fp,f_fnlist); + if (fp->f_flag & FN_AATTRNAME) + FREE(fp->f_attrname, M_TEMP); + if (fp->f_dirblbuf) + FREE(fp->f_dirblbuf, M_NTFSDIR); + FREE(fp, M_NTFSFNODE); + ntfs_ntrele(ip); +} + +/* + * Lookup attribute name in format: [[:$ATTR_TYPE]:$ATTR_NAME], + * $ATTR_TYPE is searched in attrdefs read from $AttrDefs. + * If $ATTR_TYPE not specified, ATTR_A_DATA assumed. + */ +static int +ntfs_ntlookupattr( + struct ntfsmount * ntmp, + const char * name, + int namelen, + int *attrtype, + char **attrname) +{ + const char *sys; + size_t 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++, adp++){ + if (syslen != adp->ad_namelen || + strncmp(sys, adp->ad_name, syslen) != 0) + continue; + + *attrtype = adp->ad_type; + goto out; + } + return (ENOENT); + } + + out: + if (namelen) { + *attrname = (char *) malloc(namelen, M_TEMP, M_WAITOK); + memcpy((*attrname), name, namelen); + (*attrname)[namelen] = '\0'; + *attrtype = NTFS_A_DATA; + } + + return (0); +} + +/* + * Lookup specified node for filename, matching cnp, + * return fnode filled. + */ +int +ntfs_ntlookupfile( + struct ntfsmount * ntmp, + struct vnode * vp, + struct componentname * cnp, +#ifndef __OpenBSD__ + struct vnode ** vpp) +#else + struct vnode ** vpp, + struct proc *p) +#endif +{ + struct fnode *fp = VTOF(vp); + struct ntnode *ip = FTONT(fp); + struct ntvattr *vap; /* Root attribute */ + cn_t cn = 0; /* 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; + const char *fname,*aname; + u_int32_t aoff; + int attrtype = NTFS_A_DATA; + char *attrname = NULL; + struct fnode *nfp; + struct vnode *nvp; + enum vtype f_type; + int fullscan = 0; + struct ntfs_lookup_ctx *lookup_ctx = NULL, *tctx; + +#ifndef __OpenBSD__ + error = ntfs_ntget(ip); +#else + error = ntfs_ntget(ip, p); +#endif + if (error) + return (error); + + error = ntfs_ntvattrget(ntmp, ip, NTFS_A_INDXROOT, "$I30", 0, &vap); + if (error || (vap->va_flag & NTFS_AF_INRUN)) + return (ENOTDIR); + + /* + * Divide file name into: foofilefoofilefoofile[:attrspec] + * Store like this: fname:fnamelen [aname:anamelen] + */ + 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_ntlookupfile: %s (%d), attr: %s (%d)\n", + fname, fnamelen, aname, anamelen)); + break; + } + + blsize = vap->va_a_iroot->ir_size; + dprintf(("ntfs_ntlookupfile: blksz: %d\n", blsize)); + + rdbuf = (caddr_t) malloc(blsize, M_TEMP, M_WAITOK); + + dprintf(("ntfs_ntlookupfile: blksz: %d\n", blsize, rdsize)); + + + loop: + rdsize = vap->va_datalen; + dprintf(("ntfs_ntlookupfile: rdsz: %d\n", rdsize)); + + error = ntfs_readattr(ntmp, ip, NTFS_A_INDXROOT, "$I30", + 0, rdsize, rdbuf, NULL); + if (error) + goto fail; + + aoff = sizeof(struct attr_indexroot); + + do { + iep = (struct attr_indexentry *) (rdbuf + aoff); + + for (; !(iep->ie_flag & NTFS_IEFLAG_LAST) && (rdsize > aoff); + aoff += iep->reclen, + iep = (struct attr_indexentry *) (rdbuf + aoff)) + { + ddprintf(("scan: %d, %d\n", + (u_int32_t) iep->ie_number, + (u_int32_t) iep->ie_fnametype)); + + /* check the name - the case-insensitive check + * has to come first, to break from this for loop + * if needed, so we can dive correctly */ + res = ntfs_uastricmp(ntmp, iep->ie_fname, + iep->ie_fnamelen, fname, fnamelen); + if (!fullscan) { + if (res > 0) break; + if (res < 0) continue; + } + + 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 && !fullscan) continue; + } + + /* if we perform full scan, the file does not match + * and this is subnode, dive */ + if (fullscan && res != 0) { + if (iep->ie_flag & NTFS_IEFLAG_SUBNODE) { + MALLOC(tctx, struct ntfs_lookup_ctx *, + sizeof(struct ntfs_lookup_ctx), + M_TEMP, M_WAITOK); + tctx->aoff = aoff + iep->reclen; + tctx->rdsize = rdsize; + tctx->cn = cn; + tctx->prev = lookup_ctx; + lookup_ctx = tctx; + break; + } else + continue; + } + + if (aname) { + error = ntfs_ntlookupattr(ntmp, + aname, anamelen, + &attrtype, &attrname); + if (error) + goto fail; + } + + /* Check if we've found ourselves */ + if ((iep->ie_number == ip->i_number) && + (attrtype == fp->f_attrtype) && + ((!attrname && !fp->f_attrname) || + (attrname && fp->f_attrname && + !strcmp(attrname, fp->f_attrname)))) + { + VREF(vp); + *vpp = vp; + error = 0; + goto fail; + } + + /* free the buffer returned by ntfs_ntlookupattr() */ + if (attrname) { + FREE(attrname, M_TEMP); + attrname = NULL; + } + + /* vget node, but don't load it */ + error = ntfs_vgetex(ntmp->ntm_mountp, + iep->ie_number, attrtype, attrname, + LK_EXCLUSIVE, VG_DONTLOADIN | VG_DONTVALIDFN, + curproc, &nvp); + if (error) + goto fail; + + nfp = VTOF(nvp); + + if (nfp->f_flag & FN_VALID) { + *vpp = nvp; + goto fail; + } + + nfp->f_fflag = iep->ie_fflag; + nfp->f_pnumber = iep->ie_fpnumber; + nfp->f_times = iep->ie_ftimes; + + if((nfp->f_fflag & NTFS_FFLAG_DIR) && + (nfp->f_attrtype == NTFS_A_DATA) && + (nfp->f_attrname == NULL)) + f_type = VDIR; + else + f_type = VREG; + + nvp->v_type = f_type; + + if ((nfp->f_attrtype == NTFS_A_DATA) && + (nfp->f_attrname == NULL)) + { + /* Opening default attribute */ + nfp->f_size = iep->ie_fsize; + nfp->f_allocated = iep->ie_fallocated; + nfp->f_flag |= FN_PRELOADED; + } else { + error = ntfs_filesize(ntmp, nfp, + &nfp->f_size, &nfp->f_allocated); + if (error) { + vput(nvp); + goto fail; + } + } + + nfp->f_flag &= ~FN_VALID; + *vpp = nvp; + goto fail; + } + + /* Dive if possible */ + if (iep->ie_flag & NTFS_IEFLAG_SUBNODE) { + dprintf(("ntfs_ntlookupfile: diving\n")); + + cn = *(cn_t *) (rdbuf + aoff + + iep->reclen - sizeof(cn_t)); + rdsize = blsize; + + error = ntfs_readattr(ntmp, ip, NTFS_A_INDX, "$I30", + ntfs_cntob(cn), rdsize, rdbuf, NULL); + 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 if (fullscan && lookup_ctx) { + cn = lookup_ctx->cn; + aoff = lookup_ctx->aoff; + rdsize = lookup_ctx->rdsize; + + error = ntfs_readattr(ntmp, ip, + (cn == 0) ? NTFS_A_INDXROOT : NTFS_A_INDX, + "$I30", ntfs_cntob(cn), rdsize, rdbuf, NULL); + if (error) + goto fail; + + if (cn != 0) { + error = ntfs_procfixups(ntmp, NTFS_INDXMAGIC, + rdbuf, rdsize); + if (error) + goto fail; + } + + tctx = lookup_ctx; + lookup_ctx = lookup_ctx->prev; + FREE(tctx, M_TEMP); + } else { + dprintf(("ntfs_ntlookupfile: nowhere to dive :-(\n")); + error = ENOENT; + break; + } + } while (1); + + /* perform full scan if no entry was found */ + if (!fullscan && error == ENOENT) { + fullscan = 1; + cn = 0; /* need zero, used by lookup_ctx */ + + ddprintf(("ntfs_ntlookupfile: fullscan performed for: %.*s\n", + (int) fnamelen, fname)); + goto loop; + } + + dprintf(("finish\n")); + +fail: + if (attrname) + FREE(attrname, M_TEMP); + if (lookup_ctx) { + while(lookup_ctx) { + tctx = lookup_ctx; + lookup_ctx = lookup_ctx->prev; + FREE(tctx, M_TEMP); + } + } + ntfs_ntvattrrele(vap); +#ifndef __OpenBSD__ + ntfs_ntput(ip); +#else + ntfs_ntput(ip, p); +#endif + free(rdbuf, M_TEMP); + return (error); +} + +/* + * Check if name type is permitted to show. + */ +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: skipped 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; +} + +/* + * Read ntfs dir like stream of attr_indexentry, not like btree of them. + * This is done by scanning $BITMAP:$I30 for busy clusters and reading them. + * Of course $INDEX_ROOT:$I30 is read before. Last read values are stored in + * fnode, so we can skip toward record number num almost immediately. + * Anyway this is rather slow routine. The problem is that we don't know + * how many records are there in $INDEX_ALLOCATION:$I30 block. + */ +int +ntfs_ntreaddir( + struct ntfsmount * ntmp, + struct fnode * fp, + u_int32_t num, +#ifndef __OpenBSD__ + struct attr_indexentry ** riepp) +#else + struct attr_indexentry ** riepp, + struct proc *p) +#endif +{ + struct ntnode *ip = FTONT(fp); + 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)); +#ifndef __OpenBSD__ + error = ntfs_ntget(ip); +#else + error = ntfs_ntget(ip, p); +#endif + if (error) + return (error); + + error = ntfs_ntvattrget(ntmp, ip, NTFS_A_INDXROOT, "$I30", 0, &vap); + if (error) + return (ENOTDIR); + + if (fp->f_dirblbuf == NULL) { + fp->f_dirblsz = vap->va_a_iroot->ir_size; + fp->f_dirblbuf = (caddr_t) malloc( + max(vap->va_datalen,fp->f_dirblsz), M_NTFSDIR, M_WAITOK); + } + + blsize = fp->f_dirblsz; + rdbuf = fp->f_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; + } + bmp = (u_char *) malloc(bmvap->va_datalen, M_TEMP, M_WAITOK); + error = ntfs_readattr(ntmp, ip, NTFS_A_INDXBITMAP, "$I30", 0, + bmvap->va_datalen, bmp, NULL); + 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 ((fp->f_lastdnum < num) && (fp->f_lastdnum != 0)) { + attrnum = fp->f_lastdattr; + aoff = fp->f_lastdoff; + blnum = fp->f_lastdblnum; + cnum = fp->f_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_readattr(ntmp, ip, attrnum, "$I30", + ntfs_cntob(blnum * cpbl), rdsize, rdbuf, NULL); + 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); + for (; !(iep->ie_flag & NTFS_IEFLAG_LAST) && (rdsize > aoff); + aoff += iep->reclen, + iep = (struct attr_indexentry *) (rdbuf + aoff)) + { + if (!ntfs_isnamepermitted(ntmp, iep)) continue; + + if (cnum >= num) { + fp->f_lastdnum = cnum; + fp->f_lastdoff = aoff; + fp->f_lastdblnum = blnum; + fp->f_lastdattr = attrnum; + + *riepp = iep; + + error = 0; + goto fail; + } + cnum++; + } + + 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; + fp->f_lastdnum = 0; + +fail: + if (vap) + ntfs_ntvattrrele(vap); + if (bmvap) + ntfs_ntvattrrele(bmvap); + if (iavap) + ntfs_ntvattrrele(iavap); + if (bmp) + FREE(bmp, M_TEMP); +#ifndef __OpenBSD__ + ntfs_ntput(ip); +#else + ntfs_ntput(ip, p); +#endif + return (error); +} + +/* + * Convert NTFS times that are in 100 ns units and begins from + * 1601 Jan 1 into unix times. + */ +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); +} + +#ifndef __OpenBSD__ +/* + * Get file times from NTFS_A_NAME attribute. + */ +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_ntget(ip); + if (error) + return (error); + + error = ntfs_ntvattrget(ntmp, ip, NTFS_A_NAME, NULL, 0, &vap); + if (error) { + ntfs_ntput(ip); + return (error); + } + *tm = vap->va_a_name->n_times; + ntfs_ntvattrrele(vap); + ntfs_ntput(ip); + + return (0); +} +#endif + +/* + * Get file sizes from corresponding attribute. + * + * ntnode under fnode should be locked. + */ +int +ntfs_filesize( + struct ntfsmount * ntmp, + struct fnode * fp, + u_int64_t * size, + u_int64_t * bytes) +{ + struct ntvattr *vap; + struct ntnode *ip = FTONT(fp); + u_int64_t sz, bn; + int error; + + dprintf(("ntfs_filesize: ino: %d\n", ip->i_number)); + + error = ntfs_ntvattrget(ntmp, ip, + fp->f_attrtype, fp->f_attrname, 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); +} + +/* + * This is one of write routine. + */ +int +ntfs_writeattr_plain( + struct ntfsmount * ntmp, + struct ntnode * ip, + u_int32_t attrnum, + char *attrname, + off_t roff, + size_t rsize, + void *rdata, + size_t * initp, + struct uio *uio) +{ + size_t init; + int error = 0; + off_t off = roff, left = rsize, towrite; + 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); + towrite = min(left, ntfs_cntob(vap->va_vcnend + 1) - off); + ddprintf(("ntfs_writeattr_plain: o: %d, s: %d (%d - %d)\n", + (u_int32_t) off, (u_int32_t) towrite, + (u_int32_t) vap->va_vcnstart, + (u_int32_t) vap->va_vcnend)); + error = ntfs_writentvattr_plain(ntmp, ip, vap, + off - ntfs_cntob(vap->va_vcnstart), + towrite, data, &init, uio); + if (error) { + dprintf(("ntfs_writeattr_plain: " \ + "ntfs_writentvattr_plain failed: o: %d, s: %d\n", + (u_int32_t) off, (u_int32_t) towrite)); + dprintf(("ntfs_writeattr_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 -= towrite; + off += towrite; + data = data + towrite; + *initp += init; + } + + return (error); +} + +/* + * This is one of write routine. + * + * ntnode should be locked. + */ +int +ntfs_writentvattr_plain( + struct ntfsmount * ntmp, + struct ntnode * ip, + struct ntvattr * vap, + off_t roff, + size_t rsize, + void *rdata, + size_t * initp, + struct uio *uio) +{ + int error = 0; + int off; + int cnt; + cn_t ccn, ccl, cn, left, cl; + caddr_t data = rdata; + struct buf *bp; + size_t tocopy; + + *initp = 0; + + if ((vap->va_flag & NTFS_AF_INRUN) == 0) { + dprintf(("ntfs_writevattr_plain: CAN'T WRITE RES. ATTRIBUTE\n")); + return ENOTTY; + } + + ddprintf(("ntfs_writentvattr_plain: data in run: %lu chains\n", + vap->va_vruncnt)); + + off = roff; + left = rsize; + ccl = 0; + ccn = 0; + cnt = 0; + for (; left && (cnt < vap->va_vruncnt); cnt++) { + ccn = vap->va_vruncn[cnt]; + ccl = vap->va_vruncl[cnt]; + + ddprintf(("ntfs_writentvattr_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) + continue; /* 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_writentvattr_plain: write: " \ + "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)); + if ((off == 0) && (tocopy == ntfs_cntob(cl))) + { + bp = getblk(ntmp->ntm_devvp, ntfs_cntobn(cn), + ntfs_cntob(cl), 0, 0); + clrbuf(bp); + } else { + error = bread(ntmp->ntm_devvp, ntfs_cntobn(cn), + ntfs_cntob(cl), NOCRED, &bp); + if (error) { + brelse(bp); + return (error); + } + } + if (uio) + uiomove(bp->b_data + off, tocopy, uio); + else + memcpy(bp->b_data + off, data, tocopy); + bawrite(bp); + data = data + tocopy; + *initp += tocopy; + off = 0; + left -= tocopy; + cn += cl; + ccl -= cl; + } + } + + if (left) { + printf("ntfs_writentvattr_plain: POSSIBLE RUN ERROR\n"); + error = EINVAL; + } + + return (error); +} + +/* + * This is one of read routines. + * + * ntnode should be locked. + */ +int +ntfs_readntvattr_plain( + struct ntfsmount * ntmp, + struct ntnode * ip, + struct ntvattr * vap, + off_t roff, + size_t rsize, + void *rdata, + size_t * initp, + struct uio *uio) +{ + 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_readntvattr_plain: data in run: %lu 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_readntvattr_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) { + 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); + + /* + * If 'off' pushes us to next + * block, don't attempt to read whole + * 'tocopy' at once. This is to avoid + * bread() with varying 'size' for + * same 'blkno', which is not good. + */ + if (cl > ntfs_btocl(tocopy)) { + tocopy -= + ntfs_btocnoff(tocopy + off); + cl--; + } + + ddprintf(("ntfs_readntvattr_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); + } + if (uio) { + uiomove(bp->b_data + off, + tocopy, uio); + } else { + 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_readntvattr_plain: " + "hole: 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; + if (uio) { + size_t remains = tocopy; + for(; remains; remains--) + uiomove("", 1, uio); + } else + bzero(data, tocopy); + data = data + tocopy; + } + cnt++; + } + if (left) { + printf("ntfs_readntvattr_plain: POSSIBLE RUN ERROR\n"); + error = E2BIG; + } + } else { + ddprintf(("ntfs_readnvattr_plain: data is in mft record\n")); + if (uio) + uiomove(vap->va_datap + roff, rsize, uio); + else + memcpy(rdata, vap->va_datap + roff, rsize); + *initp += rsize; + } + + return (error); +} + +/* + * This is one of read routines. + */ +int +ntfs_readattr_plain( + struct ntfsmount * ntmp, + struct ntnode * ip, + u_int32_t attrnum, + char *attrname, + off_t roff, + size_t rsize, + void *rdata, + size_t * initp, + struct uio *uio) +{ + 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_readattr_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_readntvattr_plain(ntmp, ip, vap, + off - ntfs_cntob(vap->va_vcnstart), + toread, data, &init, uio); + if (error) { + printf("ntfs_readattr_plain: " \ + "ntfs_readntvattr_plain failed: o: %d, s: %d\n", + (u_int32_t) off, (u_int32_t) toread); + printf("ntfs_readattr_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); +} + +/* + * This is one of read routines. + */ +int +ntfs_readattr( + struct ntfsmount * ntmp, + struct ntnode * ip, + u_int32_t attrnum, + char *attrname, + off_t roff, + size_t rsize, + void *rdata, + struct uio *uio) +{ + int error = 0; + struct ntvattr *vap; + size_t init; + + ddprintf(("ntfs_readattr: 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)) { + printf("ntfs_readattr: offset too big: %ld (%ld) > %ld\n", + (long int) roff, (long int) roff + rsize, + (long int) vap->va_datalen); + 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_readattr_plain(ntmp, ip, attrnum, + attrname, ntfs_cntob(cn), + ntfs_cntob(NTFS_COMPUNIT_CL), + cup, &init, NULL); + if (error) + break; + + tocopy = min(left, ntfs_cntob(NTFS_COMPUNIT_CL) - off); + + if (init == ntfs_cntob(NTFS_COMPUNIT_CL)) { + if (uio) + uiomove(cup + off, tocopy, uio); + else + memcpy(data, cup + off, tocopy); + } else if (init == 0) { + if (uio) { + size_t remains = tocopy; + for(; remains; remains--) + uiomove("", 1, uio); + } + else + bzero(data, tocopy); + } else { + error = ntfs_uncompunit(ntmp, uup, cup); + if (error) + break; + if (uio) + uiomove(uup + off, tocopy, uio); + else + 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_readattr_plain(ntmp, ip, attrnum, attrname, + roff, rsize, rdata, &init, uio); + ntfs_ntvattrrele(vap); + return (error); +} + +#if UNUSED_CODE +int +ntfs_parserun( + cn_t * cn, + cn_t * cl, + u_int8_t * run, + u_long len, + u_long *off) +{ + u_int8_t sz; + int i; + + if (NULL == run) { + printf("ntfs_parsetun: 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: sz: 0x%02x (%ld < %ld + 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: length too big: sz: 0x%02x (%ld < %ld + sz)\n", + sz, len, *off); + return (EINVAL); + } + for (i = 0; i < (sz & 0xF); i++) + *cn += (u_int32_t) run[(*off)++] << (i << 3); + + return (0); +} +#endif + +/* + * Process fixup routine on given buffer. + */ +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 %ld bytes block\n", + fhp->fh_fnum, (long)len); /* XXX printf kludge */ + 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); +} + +#if UNUSED_CODE +int +ntfs_runtocn( + cn_t * cn, + struct ntfsmount * ntmp, + u_int8_t * run, + u_long len, + cn_t vcn) +{ + cn_t ccn = 0; + cn_t ccl = 0; + u_long off = 0; + int error = 0; + +#if NTFS_DEBUG + int i; + printf("ntfs_runtocn: run: 0x%p, %ld bytes, vcn:%ld\n", + run, len, (u_long) 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); +} +#endif + +/* + * this initializes toupper table & dependant variables to be ready for + * later work + */ +void +ntfs_toupper_init() +{ + ntfs_toupper_tab = (wchar *) NULL; + lockinit(&ntfs_toupper_lock, PVFS, "ntfs_toupper", 0, 0); + ntfs_toupper_usecount = 0; +} + +/* + * if the ntfs_toupper_tab[] is filled already, just raise use count; + * otherwise read the data from the filesystem we are currently mounting + */ +int +#ifndef __OpenBSD__ +ntfs_toupper_use(mp, ntmp) + struct mount *mp; + struct ntfsmount *ntmp; +#else +ntfs_toupper_use(mp, ntmp, p) + struct mount *mp; + struct ntfsmount *ntmp; + struct proc *p; +#endif +{ + int error = 0; + struct vnode *vp; + + /* get exclusive access */ +#ifndef __OpenBSD__ + lockmgr(&ntfs_toupper_lock, LK_EXCLUSIVE, NULL); +#else + lockmgr(&ntfs_toupper_lock, LK_EXCLUSIVE, NULL, p); +#endif + + /* only read the translation data from a file if it hasn't been + * read already */ + if (ntfs_toupper_tab) + goto out; + + /* + * Read in Unicode lowercase -> uppercase translation file. + * XXX for now, just the first 256 entries are used anyway, + * so don't bother reading more + */ + MALLOC(ntfs_toupper_tab, wchar *, 256 * 256 * sizeof(wchar), + M_NTFSRDATA, M_WAITOK); + + if ((error = VFS_VGET(mp, NTFS_UPCASEINO, &vp))) + goto out; + error = ntfs_readattr(ntmp, VTONT(vp), NTFS_A_DATA, NULL, + 0, 256*256*sizeof(wchar), (char *) ntfs_toupper_tab, + NULL); + vput(vp); + + out: + ntfs_toupper_usecount++; +#ifndef __OpenBSD__ + lockmgr(&ntfs_toupper_lock, LK_RELEASE, NULL); +#else + lockmgr(&ntfs_toupper_lock, LK_RELEASE, NULL, p); +#endif + return (error); +} + +/* + * lower the use count and if it reaches zero, free the memory + * tied by toupper table + */ +void +#ifndef __OpenBSD__ +ntfs_toupper_unuse() +#else +ntfs_toupper_unuse(p) + struct proc *p; +#endif +{ + /* get exclusive access */ +#ifndef __OpenBSD__ + lockmgr(&ntfs_toupper_lock, LK_EXCLUSIVE, NULL); +#else + lockmgr(&ntfs_toupper_lock, LK_EXCLUSIVE, NULL, p); +#endif + + ntfs_toupper_usecount--; + if (ntfs_toupper_usecount == 0) { + FREE(ntfs_toupper_tab, M_NTFSRDATA); + ntfs_toupper_tab = NULL; + } +#ifdef DIAGNOSTIC + else if (ntfs_toupper_usecount < 0) { + panic("ntfs_toupper_unuse(): use count negative: %d", + ntfs_toupper_usecount); + } +#endif + + /* release the lock */ +#ifndef __OpenBSD__ + lockmgr(&ntfs_toupper_lock, LK_RELEASE, NULL); +#else + lockmgr(&ntfs_toupper_lock, LK_RELEASE, NULL, p); +#endif +} diff --git a/sys/ntfs/ntfs_subr.h b/sys/ntfs/ntfs_subr.h new file mode 100644 index 00000000000..1c40dafd078 --- /dev/null +++ b/sys/ntfs/ntfs_subr.h @@ -0,0 +1,145 @@ +/* $Id: ntfs_subr.h,v 1.1 2003/05/20 03:03:27 tedu Exp $ */ +/* $NetBSD: ntfs_subr.h,v 1.1 2002/12/23 17:38:33 jdolecek Exp $ */ + +/*- + * 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.4 1999/05/12 09:43:02 semenu Exp + */ + +#define VA_LOADED 0x0001 +#define VA_PRELOADED 0x0002 + +struct ntvattr { + LIST_ENTRY(ntvattr) va_list; + + u_int32_t va_vflag; + struct vnode *va_vp; + 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_long 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 + +#ifndef __OpenBSD__ +int ntfs_procfixups __P(( struct ntfsmount *, u_int32_t, caddr_t, size_t )); +int ntfs_parserun __P(( cn_t *, cn_t *, u_int8_t *, u_long, u_long *)); +int ntfs_runtocn __P(( cn_t *, struct ntfsmount *, u_int8_t *, u_long, cn_t)); +int ntfs_readntvattr_plain __P(( struct ntfsmount *, struct ntnode *, struct ntvattr *, off_t, size_t, void *,size_t *, struct uio *)); +int ntfs_readattr_plain __P(( struct ntfsmount *, struct ntnode *, u_int32_t, char *, off_t, size_t, void *,size_t *, struct uio *)); +int ntfs_readattr __P(( struct ntfsmount *, struct ntnode *, u_int32_t, char *, off_t, size_t, void *, struct uio *)); +int ntfs_filesize __P(( struct ntfsmount *, struct fnode *, 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 fnode *, u_int32_t, struct attr_indexentry **)); +int ntfs_runtovrun __P(( cn_t **, cn_t **, u_long *, 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_ntlookupfile __P((struct ntfsmount *, struct vnode *, struct componentname *, struct vnode **)); +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, const char *, cn_t , struct ntvattr **)); +int ntfs_ntlookup __P((struct ntfsmount *, ino_t, struct ntnode **)); +int ntfs_ntget __P((struct ntnode *)); +void ntfs_ntref __P((struct ntnode *)); +void ntfs_ntrele __P((struct ntnode *)); +void ntfs_ntput __P((struct ntnode *)); +int ntfs_loadntnode __P(( struct ntfsmount *, struct ntnode * )); +int ntfs_writentvattr_plain __P((struct ntfsmount *, struct ntnode *, struct ntvattr *, off_t, size_t, void *, size_t *, struct uio *)); +int ntfs_writeattr_plain __P((struct ntfsmount *, struct ntnode *, u_int32_t, char *, off_t, size_t, void *, size_t *, struct uio *)); +void ntfs_toupper_init __P((void)); +int ntfs_toupper_use __P((struct mount *, struct ntfsmount *)); +void ntfs_toupper_unuse __P((void)); +int ntfs_fget __P((struct ntfsmount *, struct ntnode *, int, char *, struct fnode **)); +void ntfs_frele __P((struct fnode *)); +#else +int ntfs_procfixups (struct ntfsmount *, u_int32_t, caddr_t, size_t); +int ntfs_parserun (cn_t *, cn_t *, u_int8_t *, u_long, u_long *); +int ntfs_runtocn (cn_t *, struct ntfsmount *, u_int8_t *, u_long, cn_t); +int ntfs_readntvattr_plain (struct ntfsmount *, struct ntnode *, struct ntvattr *, off_t, size_t, void *,size_t *, struct uio *); +int ntfs_readattr_plain (struct ntfsmount *, struct ntnode *, u_int32_t, char *, off_t, size_t, void *,size_t *, struct uio *); +int ntfs_readattr (struct ntfsmount *, struct ntnode *, u_int32_t, char *, off_t, size_t, void *, struct uio *); +int ntfs_filesize (struct ntfsmount *, struct fnode *, u_int64_t *, u_int64_t *); +int ntfs_times (struct ntfsmount *, struct ntnode *, ntfs_times_t *); +struct timespec ntfs_nttimetounix (u_int64_t); +int ntfs_ntreaddir (struct ntfsmount *, struct fnode *, u_int32_t, struct attr_indexentry **, struct proc *); +int ntfs_runtovrun (cn_t **, cn_t **, u_long *, u_int8_t *); +int ntfs_attrtontvattr (struct ntfsmount *, struct ntvattr **, struct attr *); +void ntfs_freentvattr (struct ntvattr *); +int ntfs_loadntvattrs (struct ntfsmount *, struct vnode *, caddr_t, struct ntvattr **); +struct ntvattr * ntfs_findntvattr (struct ntfsmount *, struct ntnode *, u_int32_t, cn_t); +int ntfs_ntlookupfile (struct ntfsmount *, struct vnode *, struct componentname *, struct vnode **, struct proc *); +int ntfs_isnamepermitted (struct ntfsmount *, struct attr_indexentry *); +int ntfs_ntvattrrele (struct ntvattr * ); +int ntfs_ntvattrget (struct ntfsmount *, struct ntnode *, u_int32_t, const char *, cn_t , struct ntvattr **); +int ntfs_ntlookup (struct ntfsmount *, ino_t, struct ntnode **, struct proc *); +int ntfs_ntget (struct ntnode *, struct proc *); +void ntfs_ntref (struct ntnode *); +void ntfs_ntrele (struct ntnode *); +void ntfs_ntput (struct ntnode *, struct proc *); +int ntfs_loadntnode ( struct ntfsmount *, struct ntnode *); +int ntfs_writentvattr_plain (struct ntfsmount *, struct ntnode *, struct ntvattr *, off_t, size_t, void *, size_t *, struct uio *); +int ntfs_writeattr_plain (struct ntfsmount *, struct ntnode *, u_int32_t, char *, off_t, size_t, void *, size_t *, struct uio *); +void ntfs_toupper_init (void); +int ntfs_toupper_use (struct mount *, struct ntfsmount *, struct proc *); +void ntfs_toupper_unuse (struct proc *); +int ntfs_fget (struct ntfsmount *, struct ntnode *, int, char *, struct fnode **); +void ntfs_frele (struct fnode *); +#endif + +/* ntfs_conv.c stuff */ +ntfs_wget_func_t ntfs_utf8_wget; +ntfs_wput_func_t ntfs_utf8_wput; +ntfs_wcmp_func_t ntfs_utf8_wcmp; diff --git a/sys/ntfs/ntfs_vfsops.c b/sys/ntfs/ntfs_vfsops.c new file mode 100644 index 00000000000..d863a914531 --- /dev/null +++ b/sys/ntfs/ntfs_vfsops.c @@ -0,0 +1,1184 @@ +/* $Id: ntfs_vfsops.c,v 1.1 2003/05/20 03:03:27 tedu Exp $ */ +/* $NetBSD: ntfs_vfsops.c,v 1.7 2003/04/24 07:50:19 christos Exp $ */ + +/*- + * 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.7 1999/05/31 11:28:30 phk Exp + */ + +#include <sys/cdefs.h> +#ifdef __KERNEL_RCSID +__KERNEL_RCSID(0, "$NetBSD: ntfs_vfsops.c,v 1.7 2003/04/24 07:50:19 christos Exp $"); +#endif + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/namei.h> +#include <sys/proc.h> +#include <sys/kernel.h> +#include <sys/vnode.h> +#include <sys/mount.h> +#include <sys/buf.h> +#include <sys/fcntl.h> +#include <sys/malloc.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/conf.h> + +#if defined(__NetBSD__) || defined(__OpenBSD__) +#include <uvm/uvm_extern.h> +#else +#include <vm/vm.h> +#endif + +#include <miscfs/specfs/specdev.h> + +/*#define NTFS_DEBUG 1*/ +#if defined(__FreeBSD__) || defined(__NetBSD__) +#include <fs/ntfs/ntfs.h> +#include <fs/ntfs/ntfs_inode.h> +#include <fs/ntfs/ntfs_subr.h> +#include <fs/ntfs/ntfs_vfsops.h> +#include <fs/ntfs/ntfs_ihash.h> +#include <fs/ntfs/ntfsmount.h> +#else +#include <ntfs/ntfs.h> +#include <ntfs/ntfs_inode.h> +#include <ntfs/ntfs_subr.h> +#include <ntfs/ntfs_vfsops.h> +#include <ntfs/ntfs_ihash.h> +#include <ntfs/ntfsmount.h> +#endif + +#ifdef MALLOC_DEFINE +MALLOC_DEFINE(M_NTFSMNT, "NTFS mount", "NTFS mount structure"); +MALLOC_DEFINE(M_NTFSNTNODE,"NTFS ntnode", "NTFS ntnode information"); +MALLOC_DEFINE(M_NTFSFNODE,"NTFS fnode", "NTFS fnode information"); +MALLOC_DEFINE(M_NTFSDIR,"NTFS dir", "NTFS dir buffer"); +#endif + +#if defined(__FreeBSD__) +static int ntfs_mount __P((struct mount *, char *, caddr_t, + struct nameidata *, struct proc *)); +#else +static int ntfs_mount __P((struct mount *, const char *, void *, + struct nameidata *, struct proc *)); +#endif +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((struct vnode *, struct mount *, + struct ntfs_args *, struct proc *)); +static int ntfs_vptofh __P((struct vnode *, struct fid *)); + +#if defined(__FreeBSD__) +static int ntfs_init __P((struct vfsconf *)); +static int ntfs_fhtovp __P((struct mount *, struct fid *, + struct sockaddr *, struct vnode **, + int *, struct ucred **)); +#elif defined(__NetBSD__) +static void ntfs_init __P((void)); +static void ntfs_reinit __P((void)); +static void ntfs_done __P((void)); +static int ntfs_fhtovp __P((struct mount *, struct fid *, + struct vnode **)); +static int ntfs_checkexp __P((struct mount *, struct mbuf *, + int *, struct ucred **)); +static int ntfs_mountroot __P((void)); +static int ntfs_sysctl __P((int *, u_int, void *, size_t *, void *, + size_t, struct proc *)); +#elif defined(__OpenBSD__) +static int ntfs_init (struct vfsconf *); +static int ntfs_fhtovp (struct mount *, struct fid *, + struct vnode **); +static int ntfs_checkexp (struct mount *, struct mbuf *, + int *, struct ucred **); +static int ntfs_sysctl (int *, u_int, void *, size_t *, void *, + size_t, struct proc *); +#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 defined(__FreeBSD__) || defined(__NetBSD__) +struct genfs_ops ntfs_genfsops = { + NULL, + NULL, + genfs_compat_gop_write, +}; +#endif + +#if defined(__NetBSD__) || defined(__OpenBSD__) +/* + * Verify a remote client has export rights and return these rights via. + * exflagsp and credanonp. + */ +static int +ntfs_checkexp(mp, nam, exflagsp, credanonp) + struct mount *mp; + struct mbuf *nam; + int *exflagsp; + struct ucred **credanonp; +{ + struct netcred *np; + struct ntfsmount *ntm = VFSTONTFS(mp); + + /* + * Get the export permission structure for this <mp, client> tuple. + */ + np = vfs_export_lookup(mp, &ntm->ntm_export, nam); + if (np == NULL) + return (EACCES); + + *exflagsp = np->netc_exflags; + *credanonp = &np->netc_anon; + return (0); +} + +/*ARGSUSED*/ +static int +ntfs_sysctl(name, namelen, oldp, oldlenp, newp, newlen, p) + int *name; + u_int namelen; + void *oldp; + size_t *oldlenp; + void *newp; + size_t newlen; + struct proc *p; +{ + return (EINVAL); +} + +#endif + +#ifdef __NetBSD__ +static int +ntfs_mountroot() +{ + struct mount *mp; + struct proc *p = curproc; /* XXX */ + int error; + struct ntfs_args args; + + if (root_device->dv_class != DV_DISK) + return (ENODEV); + + /* + * Get vnodes for rootdev. + */ + if (bdevvp(rootdev, &rootvp)) + panic("ntfs_mountroot: can't setup rootvp"); + + if ((error = vfs_rootmountalloc(MOUNT_NTFS, "root_device", &mp))) { + vrele(rootvp); + return (error); + } + + args.flag = 0; + args.uid = 0; + args.gid = 0; + args.mode = 0777; + + if ((error = ntfs_mountfs(rootvp, mp, &args, p)) != 0) { + mp->mnt_op->vfs_refcount--; + vfs_unbusy(mp); + free(mp, M_MOUNT); + vrele(rootvp); + return (error); + } + + simple_lock(&mountlist_slock); + CIRCLEQ_INSERT_TAIL(&mountlist, mp, mnt_list); + simple_unlock(&mountlist_slock); + (void)ntfs_statfs(mp, &mp->mnt_stat, p); + vfs_unbusy(mp); + return (0); +} + +static void +ntfs_init() +{ +#ifdef _LKM + malloc_type_attach(M_NTFSMNT); + malloc_type_attach(M_NTFSNTNODE); + malloc_type_attach(M_NTFSFNODE); + malloc_type_attach(M_NTFSDIR); + malloc_type_attach(M_NTFSNTHASH); + malloc_type_attach(M_NTFSNTVATTR); + malloc_type_attach(M_NTFSRDATA); + malloc_type_attach(M_NTFSDECOMP); + malloc_type_attach(M_NTFSRUN); +#endif + ntfs_nthashinit(); + ntfs_toupper_init(); +} + +static void +ntfs_reinit() +{ + ntfs_nthashreinit(); +} + +static void +ntfs_done() +{ + ntfs_nthashdone(); +#ifdef _LKM + malloc_type_detach(M_NTFSMNT); + malloc_type_detach(M_NTFSNTNODE); + malloc_type_detach(M_NTFSFNODE); + malloc_type_detach(M_NTFSDIR); + malloc_type_detach(M_NTFSNTHASH); + malloc_type_detach(M_NTFSNTVATTR); + malloc_type_detach(M_NTFSRDATA); + malloc_type_detach(M_NTFSDECOMP); + malloc_type_detach(M_NTFSRUN); +#endif +} + +#elif defined(__FreeBSD__) || defined(__OpenBSD__) + +static int +ntfs_init ( + struct vfsconf *vcp ) +{ + ntfs_nthashinit(); + ntfs_toupper_init(); + return 0; +} + +#endif /* NetBSD */ + +static int +ntfs_mount ( + struct mount *mp, +#if defined(__FreeBSD__) + char *path, + caddr_t data, +#else + const char *path, + void *data, +#endif + struct nameidata *ndp, + struct proc *p ) +{ + int err = 0; + struct vnode *devvp; + struct ntfs_args args; + size_t size; + +#ifdef __FreeBSD__ + /* + * 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*/ + + } +#endif /* FreeBSD */ + +#ifdef __NetBSD__ + if (mp->mnt_flag & MNT_GETARGS) { + struct ntfsmount *ntmp = VFSTONTFS(mp); + if (ntmp == NULL) + return EIO; + args.fspec = NULL; + args.uid = ntmp->ntm_uid; + args.gid = ntmp->ntm_gid; + args.mode = ntmp->ntm_mode; + args.flag = ntmp->ntm_flag; + vfs_showexport(mp, &args.export, &ntmp->ntm_export); + return copyout(&args, data, sizeof(args)); + } +#endif + + /* + *** + * 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) { + /* if not updating name...*/ + if (args.fspec == 0) { + /* + * Process export requests. Jumping to "success" + * will return the vfs_export() error code. + */ + struct ntfsmount *ntm = VFSTONTFS(mp); + err = vfs_export(mp, &ntm->ntm_export, &args.export); + goto success; + } + + printf("ntfs_mount(): MNT_UPDATE not supported\n"); + err = EINVAL; + goto error_1; + } + + /* + * 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; + } +#ifdef __FreeBSD__ + if (bdevsw(devvp->v_rdev) == NULL) { +#elif defined(__NetBSD__) + if (bdevsw_lookup(devvp->v_rdev) == NULL) { +#else + if (major(devvp->v_rdev) >= nblkdev) { +#endif + 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) { + err = set_statfs_info(NULL, UIO_USERSPACE, args.fspec, + UIO_USERSPACE, mp, p); + } +#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)*/ +#if defined(__FreeBSD__) || defined(__NetBSD__) + err = set_statfs_info(path, UIO_USERSPACE, args.fspec, + UIO_USERSPACE, mp, p); +#else + (void) copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, + &size); + bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size); + (void) copyinstr(args.fspec, mp->mnt_stat.f_mntfromname, + MNAMELEN - 1, &size); + bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size); + bcopy(&args, &mp->mnt_stat.mount_info.ntfs_args, sizeof(args)); +#endif + if ( !err) { + err = ntfs_mountfs(devvp, mp, &args, p); + } + } + if (err) { + goto error_2; + } + +#ifdef __FreeBSD__ +dostatfs: +#endif + /* + * 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) + 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 (ncount > 1 && devvp != rootvp) + return (EBUSY); + error = vinvalbuf(devvp, V_SAVE, p->p_ucred, p, 0, 0); + 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; + + if (strncmp(ntmp->ntm_bootfile.bf_sysid, NTFS_BBID, NTFS_BBIDLEN)) { + error = EINVAL; + dprintf(("ntfs_mountfs: invalid boot block\n")); + goto out; + } + + { + 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; + } + dprintf(("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)); + dprintf(("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; +#ifdef __OpenBSD__ + mp->mnt_data = (qaddr_t) ntmp; +#else + mp->mnt_data = ntmp; +#endif + + /* set file name encode/decode hooks XXX utf-8 only for now */ + ntmp->ntm_wget = ntfs_utf8_wget; + ntmp->ntm_wput = ntfs_utf8_wput; + ntmp->ntm_wcmp = ntfs_utf8_wcmp; + + dprintf(("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)); + + /* + * We read in some system nodes to do not allow + * reclaim them and to have everytime access to them. + */ + { + int pi[3] = { NTFS_MFTINO, NTFS_ROOTINO, NTFS_BITMAPINO }; + for (i=0; i<3; i++) { + error = VFS_VGET(mp, pi[i], &(ntmp->ntm_sysvn[pi[i]])); + if(error) + goto out1; + ntmp->ntm_sysvn[pi[i]]->v_flag |= VSYSTEM; + VREF(ntmp->ntm_sysvn[pi[i]]); + vput(ntmp->ntm_sysvn[pi[i]]); + } + } + + /* read the Unicode lowercase --> uppercase translation table, + * if necessary */ +#ifndef __OpenBSD__ + if ((error = ntfs_toupper_use(mp, ntmp))) +#else + if ((error = ntfs_toupper_use(mp, ntmp, p))) +#endif + goto out1; + + /* + * Scan $BitMap and count free clusters + */ + error = ntfs_calccfree(ntmp, &ntmp->ntm_cfree); + if(error) + goto out1; + + /* + * Read and translate to internal format attribute + * definition file. + */ + { + int num,j; + struct attrdef ad; + + /* Open $AttrDef */ + error = VFS_VGET(mp, NTFS_ATTRDEFINO, &vp ); + if(error) + goto out1; + + /* Count valid entries */ + for(num=0;;num++) { + error = ntfs_readattr(ntmp, VTONT(vp), + NTFS_A_DATA, NULL, + num * sizeof(ad), sizeof(ad), + &ad, NULL); + if (error) + goto out1; + if (ad.ad_name[0] == 0) + break; + } + + /* Alloc memory for attribute definitions */ + ntmp->ntm_ad = (struct ntvattrdef *) malloc( + num * sizeof(struct ntvattrdef), + M_NTFSMNT, M_WAITOK); + + ntmp->ntm_adnum = num; + + /* Read them and translate */ + for(i=0;i<num;i++){ + error = ntfs_readattr(ntmp, VTONT(vp), + NTFS_A_DATA, NULL, + i * sizeof(ad), sizeof(ad), + &ad, NULL); + if (error) + goto out1; + j = 0; + do { + ntmp->ntm_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; + } + + vput(vp); + } + +#if defined(__FreeBSD__) + mp->mnt_stat.f_fsid.val[0] = dev2udev(dev); + mp->mnt_stat.f_fsid.val[1] = mp->mnt_vfc->vfc_typenum; +#else + mp->mnt_stat.f_fsid.val[0] = dev; + mp->mnt_stat.f_fsid.val[1] = makefstype(MOUNT_NTFS); +#endif + mp->mnt_maxsymlinklen = 0; + mp->mnt_flag |= MNT_LOCAL; + devvp->v_specmountpoint = mp; + return (0); + +out1: + for(i=0;i<NTFS_SYSNODESNUM;i++) + if(ntmp->ntm_sysvn[i]) vrele(ntmp->ntm_sysvn[i]); + + if (vflush(mp,NULLVP,0)) + dprintf(("ntfs_mountfs: vflush failed\n")); + +out: + devvp->v_specmountpoint = NULL; + if (bp) + brelse(bp); + + /* lock the device vnode before calling VOP_CLOSE() */ + VN_LOCK(devvp, LK_EXCLUSIVE | LK_RETRY, p); + (void)VOP_CLOSE(devvp, ronly ? FREAD : FREAD|FWRITE, NOCRED, p); + VOP__UNLOCK(devvp, 0, p); + + return (error); +} + +static int +ntfs_start ( + struct mount *mp, + int flags, + struct proc *p ) +{ + return (0); +} + +static int +ntfs_unmount( + struct mount *mp, + int mntflags, + struct proc *p) +{ + struct ntfsmount *ntmp; + int error, ronly = 0, flags, i; + + dprintf(("ntfs_unmount: unmounting...\n")); + ntmp = VFSTONTFS(mp); + + flags = 0; + if(mntflags & MNT_FORCE) + flags |= FORCECLOSE; + + dprintf(("ntfs_unmount: vflushing...\n")); + error = vflush(mp,NULLVP,flags | SKIPSYSTEM); + if (error) { + dprintf(("ntfs_unmount: vflush failed: %d\n",error)); + return (error); + } + + /* Check if only system vnodes are rest */ + for(i=0;i<NTFS_SYSNODESNUM;i++) + if((ntmp->ntm_sysvn[i]) && + (ntmp->ntm_sysvn[i]->v_usecount > 1)) return (EBUSY); + + /* Dereference all system vnodes */ + for(i=0;i<NTFS_SYSNODESNUM;i++) + if(ntmp->ntm_sysvn[i]) vrele(ntmp->ntm_sysvn[i]); + + /* vflush system vnodes */ + error = vflush(mp,NULLVP,flags); + if (error) { + /* XXX should this be panic() ? */ + printf("ntfs_unmount: vflush failed(sysnodes): %d\n",error); + } + + /* Check if the type of device node isn't VBAD before + * touching v_specinfo. If the device vnode is revoked, the + * field is NULL and touching it causes null pointer derefercence. + */ + if (ntmp->ntm_devvp->v_type != VBAD) + ntmp->ntm_devvp->v_specmountpoint = NULL; + + vinvalbuf(ntmp->ntm_devvp, V_SAVE, NOCRED, p, 0, 0); + + /* lock the device vnode before calling VOP_CLOSE() */ +#ifndef __OpenBSD__ + VOP_LOCK(ntmp->ntm_devvp, LK_EXCLUSIVE | LK_RETRY); +#else + VOP_LOCK(ntmp->ntm_devvp, LK_EXCLUSIVE | LK_RETRY, p); +#endif + error = VOP_CLOSE(ntmp->ntm_devvp, ronly ? FREAD : FREAD|FWRITE, + NOCRED, p); + VOP__UNLOCK(ntmp->ntm_devvp, 0, p); + + vrele(ntmp->ntm_devvp); + + /* free the toupper table, if this has been last mounted ntfs volume */ +#ifndef __OpenBSD__ + ntfs_toupper_unuse(); +#else + ntfs_toupper_unuse(p); +#endif + + dprintf(("ntfs_umount: freeing memory...\n")); + mp->mnt_data = NULL; + mp->mnt_flag &= ~MNT_LOCAL; + free(ntmp->ntm_ad, 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(): sysvn: %p\n", + VFSTONTFS(mp)->ntm_sysvn[NTFS_ROOTINO])); + 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; +} + +int +ntfs_calccfree( + struct ntfsmount *ntmp, + cn_t *cfreep) +{ + struct vnode *vp; + u_int8_t *tmp; + int j, error; + long cfree = 0; + size_t bmsize, i; + + vp = ntmp->ntm_sysvn[NTFS_BITMAPINO]; + + bmsize = VTOF(vp)->f_size; + + tmp = (u_int8_t *) malloc(bmsize, M_TEMP, M_WAITOK); + + error = ntfs_readattr(ntmp, VTONT(vp), NTFS_A_DATA, NULL, + 0, bmsize, tmp, NULL); + if (error) + goto out; + + for(i=0;i<bmsize;i++) + for(j=0;j<8;j++) + if(~tmp[i] & (1 << j)) cfree++; + *cfreep = cfree; + + out: + free(tmp, M_TEMP); + return(error); +} + +static int +ntfs_statfs( + struct mount *mp, + struct statfs *sbp, + struct proc *p) +{ + struct ntfsmount *ntmp = VFSTONTFS(mp); + u_int64_t mftallocated; + + dprintf(("ntfs_statfs():\n")); + + mftallocated = VTOF(ntmp->ntm_sysvn[NTFS_MFTINO])->f_allocated; + +#if defined(__FreeBSD__) + sbp->f_type = mp->mnt_vfc->vfc_typenum; +#elif defined(__NetBSD__) + sbp->f_type = 0; +#elif !defined(__OpenBSD__) + 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(ntmp->ntm_cfree); + sbp->f_ffree = sbp->f_bfree / ntmp->ntm_bpmftrec; + sbp->f_files = mftallocated / ntfs_bntob(ntmp->ntm_bpmftrec) + + sbp->f_ffree; + sbp->f_flags = mp->mnt_flag; +#if !defined(__OpenBSD__) + copy_statfs_info(sbp, mp); +#else + if (sbp != &mp->mnt_stat) { + bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN); + bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN); + bcopy(&mp->mnt_stat.mount_info.msdosfs_args, + &sbp->mount_info.msdosfs_args, sizeof(struct msdosfs_args)); + } + strncpy(sbp->f_fstypename, mp->mnt_vfc->vfc_name, MFSNAMELEN); +#endif + return (0); +} + +static int +ntfs_sync ( + struct mount *mp, + int waitfor, + struct ucred *cred, + struct proc *p) +{ + /*dprintf(("ntfs_sync():\n"));*/ + return (0); +} + +/*ARGSUSED*/ +static int +ntfs_fhtovp( +#if defined(__FreeBSD__) + struct mount *mp, + struct fid *fhp, + struct sockaddr *nam, + struct vnode **vpp, + int *exflagsp, + struct ucred **credanonp) +#elif defined(__NetBSD__) || defined(__OpenBSD__) + struct mount *mp, + struct fid *fhp, + struct vnode **vpp) +#else + struct mount *mp, + struct fid *fhp, + struct mbuf *nam, + struct vnode **vpp, + int *exflagsp, + struct ucred **credanonp) +#endif +{ + struct ntfid *ntfhp = (struct ntfid *)fhp; + int error; + + ddprintf(("ntfs_fhtovp(): %s: %d\n", mp->mnt_stat.f_mntonname, + ntfhp->ntfid_ino)); + + error = ntfs_vgetex(mp, ntfhp->ntfid_ino, ntfhp->ntfid_attr, NULL, + LK_EXCLUSIVE | LK_RETRY, 0, curproc, vpp); /* XXX */ + if (error != 0) { + *vpp = NULLVP; + return (error); + } + + /* XXX as unlink/rmdir/mkdir/creat are not currently possible + * with NTFS, we don't need to check anything else for now */ + return (0); +} + +static int +ntfs_vptofh( + struct vnode *vp, + struct fid *fhp) +{ + struct ntnode *ntp; + struct ntfid *ntfhp; + struct fnode *fn; + + ddprintf(("ntfs_fhtovp(): %s: %p\n", vp->v_mount->mnt_stat.f_mntonname, + vp)); + + fn = VTOF(vp); + ntp = VTONT(vp); + ntfhp = (struct ntfid *)fhp; + ntfhp->ntfid_len = sizeof(struct ntfid); + ntfhp->ntfid_ino = ntp->i_number; + ntfhp->ntfid_attr = fn->f_attrtype; +#ifdef notyet + ntfhp->ntfid_gen = ntp->i_gen; +#endif + return (0); +} + +int +ntfs_vgetex( + struct mount *mp, + ino_t ino, + u_int32_t attrtype, + char *attrname, + u_long lkflags, + u_long flags, + struct proc *p, + struct vnode **vpp) +{ + int error; + struct ntfsmount *ntmp; + struct ntnode *ip; + struct fnode *fp; + struct vnode *vp; + enum vtype f_type; + + dprintf(("ntfs_vgetex: ino: %d, attr: 0x%x:%s, lkf: 0x%lx, f: 0x%lx\n", + ino, attrtype, attrname?attrname:"", (u_long)lkflags, + (u_long)flags )); + + ntmp = VFSTONTFS(mp); + *vpp = NULL; + + /* Get ntnode */ +#ifndef __OpenBSD__ + error = ntfs_ntlookup(ntmp, ino, &ip); +#else + error = ntfs_ntlookup(ntmp, ino, &ip, p); +#endif + if (error) { + printf("ntfs_vget: ntfs_ntget failed\n"); + return (error); + } + + /* It may be not initialized fully, so force load it */ + if (!(flags & VG_DONTLOADIN) && !(ip->i_flag & IN_LOADED)) { + error = ntfs_loadntnode(ntmp, ip); + if(error) { + printf("ntfs_vget: CAN'T LOAD ATTRIBUTES FOR INO: %d\n", + ip->i_number); +#ifndef __OpenBSD__ + ntfs_ntput(ip); +#else + ntfs_ntput(ip, p); +#endif + return (error); + } + } + + error = ntfs_fget(ntmp, ip, attrtype, attrname, &fp); + if (error) { + printf("ntfs_vget: ntfs_fget failed\n"); +#ifndef __OpenBSD__ + ntfs_ntput(ip); +#else + ntfs_ntput(ip, p); +#endif + return (error); + } + + if (!(flags & VG_DONTVALIDFN) && !(fp->f_flag & FN_VALID)) { + if ((ip->i_frflag & NTFS_FRFLAG_DIR) && + (fp->f_attrtype == NTFS_A_DATA && fp->f_attrname == NULL)) { + f_type = VDIR; + } else if (flags & VG_EXT) { + f_type = VNON; + fp->f_size = fp->f_allocated = 0; + } else { + f_type = VREG; + + error = ntfs_filesize(ntmp, fp, + &fp->f_size, &fp->f_allocated); + if (error) { +#ifndef __OpenBSD__ + ntfs_ntput(ip); +#else + ntfs_ntput(ip, p); +#endif + return (error); + } + } + + fp->f_flag |= FN_VALID; + } + + /* + * We may be calling vget() now. To avoid potential deadlock, we need + * to release ntnode lock, since due to locking order vnode + * lock has to be acquired first. + * ntfs_fget() bumped ntnode usecount, so ntnode won't be recycled + * prematurely. + */ +#ifndef __OpenBSD__ + ntfs_ntput(ip); +#else + ntfs_ntput(ip, p); +#endif + + if (FTOV(fp)) { + /* vget() returns error if the vnode has been recycled */ + if (VGET(FTOV(fp), lkflags, p) == 0) { + *vpp = FTOV(fp); + return (0); + } + } + + error = getnewvnode(VT_NTFS, ntmp->ntm_mountp, ntfs_vnodeop_p, &vp); + if(error) { + ntfs_frele(fp); +#ifndef __OpenBSD__ + ntfs_ntput(ip); +#else + ntfs_ntput(ip, p); +#endif + return (error); + } + dprintf(("ntfs_vget: vnode: %p for ntnode: %d\n", vp,ino)); + +#ifdef __FreeBSD__ + lockinit(&fp->f_lock, PINOD, "fnode", 0, 0); +#endif + fp->f_vp = vp; + vp->v_data = fp; + vp->v_type = f_type; + + if (ino == NTFS_ROOTINO) + vp->v_flag |= VROOT; + + if (lkflags & LK_TYPE_MASK) { + error = VN_LOCK(vp, lkflags, p); + if (error) { + vput(vp); + return (error); + } + } + +#if defined(__FreeBSD__) || defined(__NetBSD__) + genfs_node_init(vp, &ntfs_genfsops); +#endif + VREF(ip->i_devvp); + *vpp = vp; + return (0); +} + +static int +ntfs_vget( + struct mount *mp, + ino_t ino, + struct vnode **vpp) +{ + return ntfs_vgetex(mp, ino, NTFS_A_DATA, NULL, + LK_EXCLUSIVE | LK_RETRY, 0, curproc, vpp); /* XXX */ +} + +#if defined(__FreeBSD__) +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 +}; +VFS_SET(ntfs_vfsops, ntfs, 0); +#elif defined(__NetBSD__) +extern const struct vnodeopv_desc ntfs_vnodeop_opv_desc; + +const struct vnodeopv_desc * const ntfs_vnodeopv_descs[] = { + &ntfs_vnodeop_opv_desc, + NULL, +}; + +struct vfsops ntfs_vfsops = { + MOUNT_NTFS, + ntfs_mount, + ntfs_start, + ntfs_unmount, + ntfs_root, + ntfs_quotactl, + ntfs_statfs, + ntfs_sync, + ntfs_vget, + ntfs_fhtovp, + ntfs_vptofh, + ntfs_init, + ntfs_reinit, + ntfs_done, + ntfs_sysctl, + ntfs_mountroot, + ntfs_checkexp, + ntfs_vnodeopv_descs, +}; +#elif defined(__OpenBSD__) +extern const struct vnodeopv_desc ntfs_vnodeop_opv_desc; + +const struct vnodeopv_desc * const ntfs_vnodeopv_descs[] = { + &ntfs_vnodeop_opv_desc, + NULL, +}; + +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, + ntfs_sysctl, + ntfs_checkexp, +}; + +#endif diff --git a/sys/ntfs/ntfs_vfsops.h b/sys/ntfs/ntfs_vfsops.h new file mode 100644 index 00000000000..7f4f30ebc0c --- /dev/null +++ b/sys/ntfs/ntfs_vfsops.h @@ -0,0 +1,40 @@ +/* $Id: ntfs_vfsops.h,v 1.1 2003/05/20 03:03:27 tedu Exp $ */ +/* $NetBSD: ntfs_vfsops.h,v 1.1 2002/12/23 17:38:34 jdolecek Exp $ */ + +/*- + * Copyright (c) 1998, 1999 Semen Ustimenko (semenu@FreeBSD.org) + * 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.h,v 1.3 1999/05/12 09:43:06 semenu Exp + */ +#define VG_DONTLOADIN 0x0001 /* Tells ntfs_vgetex to do not call */ + /* ntfs_loadntnode() on ntnode, even if */ + /* ntnode not loaded */ +#define VG_DONTVALIDFN 0x0002 /* Tells ntfs_vgetex to do not validate */ + /* fnode */ +#define VG_EXT 0x0004 /* This is not main record */ + +int ntfs_vgetex(struct mount *, ino_t, u_int32_t, char *, u_long, u_long, + struct proc *, struct vnode **); +int ntfs_calccfree(struct ntfsmount *, cn_t *); diff --git a/sys/ntfs/ntfs_vnops.c b/sys/ntfs/ntfs_vnops.c new file mode 100644 index 00000000000..0e4688531d7 --- /dev/null +++ b/sys/ntfs/ntfs_vnops.c @@ -0,0 +1,1074 @@ +/* $Id: ntfs_vnops.c,v 1.1 2003/05/20 03:03:27 tedu Exp $ */ +/* $NetBSD: ntfs_vnops.c,v 1.6 2003/04/10 21:57:26 jdolecek Exp $ */ + +/* + * 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.5 1999/05/12 09:43:06 semenu Exp + * + */ + +#include <sys/cdefs.h> +#ifdef __KERNEL_RCSID +__KERNEL_RCSID(0, "$NetBSD: ntfs_vnops.c,v 1.6 2003/04/10 21:57:26 jdolecek Exp $"); +#endif + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <sys/vnode.h> +#include <sys/mount.h> +#include <sys/namei.h> +#include <sys/malloc.h> +#include <sys/buf.h> +#include <sys/dirent.h> + +#if !defined(__NetBSD__) && !defined(__OpenBSD__) +#include <vm/vm.h> +#endif + +#if defined(__FreeBSD__) +#include <vm/vnode_pager.h> +#endif + +#include <sys/sysctl.h> + + +/*#define NTFS_DEBUG 1*/ +#if defined(__FreeBSD__) || defined(__NetBSD__) +#include <fs/ntfs/ntfs.h> +#include <fs/ntfs/ntfs_inode.h> +#include <fs/ntfs/ntfs_subr.h> +#include <miscfs/genfs/genfs.h> +#else +#include <ntfs/ntfs.h> +#include <ntfs/ntfs_inode.h> +#include <ntfs/ntfs_subr.h> +#endif + +#include <miscfs/specfs/specdev.h> + +#include <sys/unistd.h> /* for pathconf(2) constants */ + +static int ntfs_bypass (struct vop_generic_args *ap); +static int ntfs_read (struct vop_read_args *); +static int ntfs_write (struct vop_write_args *ap); +static int ntfs_getattr (struct vop_getattr_args *ap); +static int ntfs_inactive (struct vop_inactive_args *ap); +static int ntfs_print (struct vop_print_args *ap); +static int ntfs_reclaim (struct vop_reclaim_args *ap); +static int ntfs_strategy (struct vop_strategy_args *ap); +static int ntfs_access (struct vop_access_args *ap); +static int ntfs_open (struct vop_open_args *ap); +static int ntfs_close (struct vop_close_args *ap); +static int ntfs_readdir (struct vop_readdir_args *ap); +static int ntfs_lookup (struct vop_lookup_args *ap); +static int ntfs_bmap (struct vop_bmap_args *ap); +#if defined(__FreeBSD__) +static int ntfs_getpages (struct vop_getpages_args *ap); +static int ntfs_putpages (struct vop_putpages_args *); +#endif +#if defined(__FreeBSD__) || defined(__OpenBSD__) +static int ntfs_fsync (struct vop_fsync_args *ap); +#endif +static int ntfs_pathconf (void *); + +int ntfs_prtactive = 1; /* 1 => print out reclaim of active vnodes */ + +#if defined(__FreeBSD__) +int +ntfs_getpages(ap) + struct vop_getpages_args *ap; +{ + return vnode_pager_generic_getpages(ap->a_vp, ap->a_m, ap->a_count, + ap->a_reqpage); +} + +int +ntfs_putpages(ap) + struct vop_putpages_args *ap; +{ + return vnode_pager_generic_putpages(ap->a_vp, ap->a_m, ap->a_count, + ap->a_sync, ap->a_rtvals); +} +#endif + +/* + * 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; +{ + dprintf(("ntfs_bmap: vn: %p, blk: %d\n", ap->a_vp,(u_int32_t)ap->a_bn)); + 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 !defined(__NetBSD__) && !defined(__OpenBSD__) + if (ap->a_runb != NULL) + *ap->a_runb = 0; +#endif + 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; +{ + struct vnode *vp = ap->a_vp; + struct fnode *fp = VTOF(vp); + struct ntnode *ip = FTONT(fp); + struct uio *uio = ap->a_uio; + struct ntfsmount *ntmp = ip->i_mp; + 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)); + + dprintf(("ntfs_read: filesize: %d",(u_int32_t)fp->f_size)); + + /* don't allow reading after end of file */ + if (uio->uio_offset > fp->f_size) + toread = 0; + else + toread = min( uio->uio_resid, fp->f_size - uio->uio_offset ); + + dprintf((", toread: %d\n",(u_int32_t)toread)); + + if (toread == 0) + return (0); + + error = ntfs_readattr(ntmp, ip, fp->f_attrtype, + fp->f_attrname, uio->uio_offset, toread, NULL, uio); + if (error) { + printf("ntfs_read: ntfs_readattr failed: %d\n",error); + return (error); + } + + return (0); +} + +static int +ntfs_bypass(ap) + struct vop_generic_args /* { + struct vnodeop_desc *a_desc; + <other random data follows, presumably> + } */ *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; +{ + struct vnode *vp = ap->a_vp; + struct fnode *fp = VTOF(vp); + struct ntnode *ip = FTONT(fp); + struct vattr *vap = ap->a_vap; + + dprintf(("ntfs_getattr: %d, flags: %d\n",ip->i_number,ip->i_flag)); + +#if defined(__FreeBSD__) + vap->va_fsid = dev2udev(ip->i_dev); +#else /* NetBSD */ + vap->va_fsid = ip->i_dev; +#endif + vap->va_fileid = ip->i_number; + vap->va_mode = ip->i_mp->ntm_mode; + vap->va_nlink = ip->i_nlink; + vap->va_uid = ip->i_mp->ntm_uid; + vap->va_gid = ip->i_mp->ntm_gid; + vap->va_rdev = 0; /* XXX UNODEV ? */ + vap->va_size = fp->f_size; + vap->va_bytes = fp->f_allocated; + vap->va_atime = ntfs_nttimetounix(fp->f_times.t_access); + vap->va_mtime = ntfs_nttimetounix(fp->f_times.t_write); + vap->va_ctime = ntfs_nttimetounix(fp->f_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 = vp->v_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; + struct proc *a_p; + } */ *ap; +{ + struct vnode *vp = ap->a_vp; +#ifdef __OpenBSD__ + struct proc *p = ap->a_p; +#endif +#ifdef NTFS_DEBUG + struct ntnode *ip = VTONT(vp); +#endif + + dprintf(("ntfs_inactive: vnode: %p, ntnode: %d\n", vp, ip->i_number)); + + if (ntfs_prtactive && vp->v_usecount != 0) + vprint("ntfs_inactive: pushing active", vp); + + VOP__UNLOCK(vp, 0, p); + + /* XXX since we don't support any filesystem changes + * right now, nothing more needs to be done + */ + return (0); +} + +/* + * Reclaim an fnode/ntnode so that it can be used for other purposes. + */ +int +ntfs_reclaim(ap) + struct vop_reclaim_args /* { + struct vnode *a_vp; + struct proc *a_p; + } */ *ap; +{ + struct vnode *vp = ap->a_vp; + struct fnode *fp = VTOF(vp); + struct ntnode *ip = FTONT(fp); +#ifdef __OpenBSD__ + struct proc *p = ap->a_p; +#endif + int error; + + dprintf(("ntfs_reclaim: vnode: %p, ntnode: %d\n", vp, ip->i_number)); + + if (ntfs_prtactive && vp->v_usecount != 0) + vprint("ntfs_reclaim: pushing active", vp); + +#ifndef __OpenBSD__ + if ((error = ntfs_ntget(ip)) != 0) +#else + if ((error = ntfs_ntget(ip, p)) != 0) +#endif + return (error); + + /* Purge old data structures associated with the inode. */ + cache_purge(vp); + if (ip->i_devvp) { + vrele(ip->i_devvp); + ip->i_devvp = NULL; + } + + ntfs_frele(fp); +#ifndef __OpenBSD__ + ntfs_ntput(ip); +#else + ntfs_ntput(ip, p); +#endif + vp->v_data = NULL; + + return (0); +} + +static int +ntfs_print(ap) + struct vop_print_args /* { + struct vnode *a_vp; + } */ *ap; +{ + struct ntnode *ip = VTONT(ap->a_vp); + + printf("tag VT_NTFS, ino %u, flag %#x, usecount %d, nlink %ld\n", + ip->i_number, ip->i_flag, ip->i_usecount, ip->i_nlink); + printf(" "); + lockmgr_printinfo(ap->a_vp->v_vnlock); + printf("\n"); + 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; +{ + struct buf *bp = ap->a_bp; + struct vnode *vp = bp->b_vp; + struct fnode *fp = VTOF(vp); + struct ntnode *ip = FTONT(fp); + struct ntfsmount *ntmp = ip->i_mp; + int error; + +#ifdef __FreeBSD__ + dprintf(("ntfs_strategy: offset: %d, blkno: %d, lblkno: %d\n", + (u_int32_t)bp->b_offset,(u_int32_t)bp->b_blkno, + (u_int32_t)bp->b_lblkno)); +#else + dprintf(("ntfs_strategy: blkno: %d, lblkno: %d\n", + (u_int32_t)bp->b_blkno, + (u_int32_t)bp->b_lblkno)); +#endif + + dprintf(("strategy: bcount: %d flags: 0x%lx\n", + (u_int32_t)bp->b_bcount,bp->b_flags)); + + if (bp->b_flags & B_READ) { + u_int32_t toread; + + if (ntfs_cntob(bp->b_blkno) >= fp->f_size) { + clrbuf(bp); + error = 0; + } else { + toread = min(bp->b_bcount, + fp->f_size-ntfs_cntob(bp->b_blkno)); + dprintf(("ntfs_strategy: toread: %d, fsize: %d\n", + toread,(u_int32_t)fp->f_size)); + + error = ntfs_readattr(ntmp, ip, fp->f_attrtype, + fp->f_attrname, ntfs_cntob(bp->b_blkno), + toread, bp->b_data, NULL); + + if (error) { + printf("ntfs_strategy: ntfs_readattr failed\n"); + bp->b_error = error; + bp->b_flags |= B_ERROR; + } + + bzero(bp->b_data + toread, bp->b_bcount - toread); + } + } else { + size_t tmp; + u_int32_t towrite; + + if (ntfs_cntob(bp->b_blkno) + bp->b_bcount >= fp->f_size) { + printf("ntfs_strategy: CAN'T EXTEND FILE\n"); + bp->b_error = error = EFBIG; + bp->b_flags |= B_ERROR; + } else { + towrite = min(bp->b_bcount, + fp->f_size-ntfs_cntob(bp->b_blkno)); + dprintf(("ntfs_strategy: towrite: %d, fsize: %d\n", + towrite,(u_int32_t)fp->f_size)); + + error = ntfs_writeattr_plain(ntmp, ip, fp->f_attrtype, + fp->f_attrname, ntfs_cntob(bp->b_blkno),towrite, + bp->b_data, &tmp, NULL); + + if (error) { + printf("ntfs_strategy: ntfs_writeattr fail\n"); + bp->b_error = error; + bp->b_flags |= B_ERROR; + } + } + } + biodone(bp); + return (error); +} + +static int +ntfs_write(ap) + struct vop_write_args /* { + struct vnode *a_vp; + struct uio *a_uio; + int a_ioflag; + struct ucred *a_cred; + } */ *ap; +{ + struct vnode *vp = ap->a_vp; + struct fnode *fp = VTOF(vp); + struct ntnode *ip = FTONT(fp); + struct uio *uio = ap->a_uio; + struct ntfsmount *ntmp = ip->i_mp; + u_int64_t towrite; + size_t written; + int error; + + dprintf(("ntfs_write: ino: %d, off: %d resid: %d, segflg: %d\n",ip->i_number,(u_int32_t)uio->uio_offset,uio->uio_resid,uio->uio_segflg)); + dprintf(("ntfs_write: filesize: %d",(u_int32_t)fp->f_size)); + + if (uio->uio_resid + uio->uio_offset > fp->f_size) { + printf("ntfs_write: CAN'T WRITE BEYOND END OF FILE\n"); + return (EFBIG); + } + + towrite = min(uio->uio_resid, fp->f_size - uio->uio_offset); + + dprintf((", towrite: %d\n",(u_int32_t)towrite)); + + error = ntfs_writeattr_plain(ntmp, ip, fp->f_attrtype, + fp->f_attrname, uio->uio_offset, towrite, NULL, &written, uio); +#ifdef NTFS_DEBUG + if (error) + printf("ntfs_write: ntfs_writeattr failed: %d\n", error); +#endif + + return (error); +} + +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; + gid_t *gp; + int i; +#if defined(__NetBSD__) && defined(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 ((int)vp->v_type) { + case VDIR: + case VLNK: + case VREG: + if (vp->v_mount->mnt_flag & MNT_RDONLY) + return (EROFS); +#if defined(__NetBSD__) && defined(QUOTA) + if (error = getinoquota(ip)) + return (error); +#endif + break; + } + } + + /* 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_mp->ntm_uid) { + if (mode & VEXEC) + mask |= S_IXUSR; + if (mode & VREAD) + mask |= S_IRUSR; + if (mode & VWRITE) + mask |= S_IWUSR; + return ((ip->i_mp->ntm_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_mp->ntm_gid == *gp) { + if (mode & VEXEC) + mask |= S_IXGRP; + if (mode & VREAD) + mask |= S_IRGRP; + if (mode & VWRITE) + mask |= S_IWGRP; + return ((ip->i_mp->ntm_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_mp->ntm_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 + struct vnode *vp = ap->a_vp; + struct ntnode *ip = VTONT(vp); + + printf("ntfs_open: %d\n",ip->i_number); +#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 + struct vnode *vp = ap->a_vp; + struct ntnode *ip = VTONT(vp); + + printf("ntfs_close: %d\n",ip->i_number); +#endif + + return (0); +} + +int +ntfs_readdir(ap) + struct vop_readdir_args /* { + struct vnode *a_vp; + struct uio *a_uio; + struct ucred *a_cred; + int *a_eofflag; + int *a_ncookies; + u_long **a_cookies; + } */ *ap; +{ + struct vnode *vp = ap->a_vp; + struct fnode *fp = VTOF(vp); + struct ntnode *ip = FTONT(fp); + 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; + + MALLOC(cde, struct dirent *, sizeof(struct dirent), M_TEMP, M_WAITOK); + + /* Simulate . in every dir except ROOT */ + if (ip->i_number != NTFS_ROOTINO + && uio->uio_offset < sizeof(struct dirent)) { + cde->d_fileno = ip->i_number; + cde->d_reclen = sizeof(struct dirent); + cde->d_type = DT_DIR; + cde->d_namlen = 1; + strncpy(cde->d_name, ".", 2); + error = uiomove((void *)cde, sizeof(struct dirent), uio); + if (error) + goto out; + + ncookies++; + } + + /* Simulate .. in every dir including ROOT */ + if (uio->uio_offset < 2 * sizeof(struct dirent)) { + cde->d_fileno = NTFS_ROOTINO; /* XXX */ + cde->d_reclen = sizeof(struct dirent); + cde->d_type = DT_DIR; + cde->d_namlen = 2; + strncpy(cde->d_name, "..", 3); + + error = uiomove((void *) cde, sizeof(struct dirent), uio); + if (error) + goto out; + + 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; + char *fname; + size_t remains; + int sz; + +#ifndef __OpenBSD__ + error = ntfs_ntreaddir(ntmp, fp, num, &iep); +#else + error = ntfs_ntreaddir(ntmp, fp, num, &iep, uio->uio_procp); +#endif + if (error) + goto out; + + if (NULL == iep) + break; + + for(; !(iep->ie_flag & NTFS_IEFLAG_LAST) && (uio->uio_resid >= sizeof(struct dirent)); + iep = NTFS_NEXTREC(iep, struct attr_indexentry *)) + { + if(!ntfs_isnamepermitted(ntmp,iep)) + continue; + + remains = sizeof(cde->d_name) - 1; + fname = cde->d_name; + for(i=0; i<iep->ie_fnamelen; i++) { + sz = (*ntmp->ntm_wput)(fname, remains, + iep->ie_fname[i]); + fname += sz; + remains -= sz; + } + *fname = '\0'; + dprintf(("ntfs_readdir: elem: %d, fname:[%s] type: %d, flag: %d, ", + num, cde->d_name, iep->ie_fnametype, + iep->ie_flag)); + cde->d_namlen = fname - (char *) cde->d_name; + 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((void *)cde, sizeof(struct dirent), uio); + if (error) + goto out; + + ncookies++; + num++; + } + } + + 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 defined(__FreeBSD__) || defined(__OpenBSD__) + u_long *cookies; + u_long *cookiep; +#else /* defined(__NetBSD__) */ + off_t *cookies; + off_t *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 *) + ((caddr_t)uio->uio_iov->iov_base - + (uio->uio_offset - off)); +#if defined(__FreeBSD__) + MALLOC(cookies, u_long *, ncookies * sizeof(u_long), + M_TEMP, M_WAITOK); +#else /* defined(__NetBSD__) */ + cookies = malloc(ncookies * sizeof(off_t), 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; +*/ + out: + FREE(cde, M_TEMP); + return (error); +} + +int +ntfs_lookup(ap) + struct vop_lookup_args /* { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + } */ *ap; +{ + struct vnode *dvp = ap->a_dvp; + 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; +#ifdef __OpenBSD__ + struct proc *p = cnp->cn_proc; +#endif +#if NTFS_DEBUG + int wantparent = cnp->cn_flags & (LOCKPARENT|WANTPARENT); +#endif + dprintf(("ntfs_lookup: \"%.*s\" (%ld bytes) in %d, lp: %d, wp: %d \n", + (int)cnp->cn_namelen, 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_flags & ISLASTCN) && + (dvp->v_mount->mnt_flag & MNT_RDONLY) && + (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) + return (EROFS); + +#if defined(__NetBSD__) || defined(__OpenBSD__) + /* + * We now have a segment name to search for, and a directory + * to search. + * + * Before tediously performing a linear scan of the directory, + * check the name cache to see if the directory/name pair + * we are looking for is known already. + */ + if ((error = cache_lookup(ap->a_dvp, ap->a_vpp, cnp)) >= 0) + return (error); +#endif + + if(cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') { + dprintf(("ntfs_lookup: faking . directory in %d\n", + dip->i_number)); + + VREF(dvp); + *ap->a_vpp = dvp; + error = 0; + } else if (cnp->cn_flags & ISDOTDOT) { + struct ntvattr *vap; + + dprintf(("ntfs_lookup: faking .. directory in %d\n", + dip->i_number)); + + VOP__UNLOCK(dvp, 0, p); + cnp->cn_flags |= PDIRUNLOCK; + + error = ntfs_ntvattrget(ntmp, dip, NTFS_A_NAME, NULL, 0, &vap); + if(error) + return (error); + + 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 (VN_LOCK(dvp, LK_EXCLUSIVE | LK_RETRY, p) == 0) + cnp->cn_flags &= ~PDIRUNLOCK; + return (error); + } + + if (lockparent && (cnp->cn_flags & ISLASTCN)) { + error = VN_LOCK(dvp, LK_EXCLUSIVE, p); + if (error) { + vput( *(ap->a_vpp) ); + return (error); + } + cnp->cn_flags &= ~PDIRUNLOCK; + } + } else { +#ifndef __OpenBSD__ + error = ntfs_ntlookupfile(ntmp, dvp, cnp, ap->a_vpp); +#else + error = ntfs_ntlookupfile(ntmp, dvp, cnp, ap->a_vpp, p); +#endif + if (error) { + dprintf(("ntfs_ntlookupfile: returned %d\n", error)); + return (error); + } + + dprintf(("ntfs_lookup: found ino: %d\n", + VTONT(*ap->a_vpp)->i_number)); + + if(!lockparent || (cnp->cn_flags & ISLASTCN) == 0) { + VOP__UNLOCK(dvp, 0, p); + cnp->cn_flags |= PDIRUNLOCK; + } + } + + if (cnp->cn_flags & MAKEENTRY) + cache_enter(dvp, *ap->a_vpp, cnp); + + return (error); +} + +#if defined(__FreeBSD__) || defined(__OpenBSD__) +/* + * 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; + off_t offlo; + off_t offhi; + struct proc *a_p; + } */ *ap; +{ + return (0); +} +#endif + +/* + * Return POSIX pathconf information applicable to NTFS filesystem + */ +static int +ntfs_pathconf(v) + void *v; +{ + struct vop_pathconf_args /* { + struct vnode *a_vp; + int a_name; + register_t *a_retval; + } */ *ap = v; + + switch (ap->a_name) { + case _PC_LINK_MAX: + *ap->a_retval = 1; + return (0); + case _PC_NAME_MAX: + *ap->a_retval = NTFS_MAXFILENAME; + return (0); + case _PC_PATH_MAX: + *ap->a_retval = PATH_MAX; + return (0); + case _PC_CHOWN_RESTRICTED: + *ap->a_retval = 1; + return (0); + case _PC_NO_TRUNC: + *ap->a_retval = 0; + return (0); +#ifdef __NetBSD__ + case _PC_SYNC_IO: + *ap->a_retval = 1; + return (0); + case _PC_FILESIZEBITS: + *ap->a_retval = 64; + return (0); +#endif + default: + return (EINVAL); + } + /* NOTREACHED */ +} + +/* + * Global vfs data structures + */ +vop_t **ntfs_vnodeop_p; +#if defined(__FreeBSD__) +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 }, + { &vop_pathconf_desc, ntfs_pathconf }, + + { &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 }, + + { &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_putpages_desc, (vop_t *) ntfs_putpages }, + { &vop_strategy_desc, (vop_t *)ntfs_strategy }, + { &vop_bwrite_desc, (vop_t *)vop_stdbwrite }, + { &vop_read_desc, (vop_t *)ntfs_read }, + { &vop_write_desc, (vop_t *)ntfs_write }, + + { NULL, NULL } +}; + +static +struct vnodeopv_desc ntfs_vnodeop_opv_desc = + { &ntfs_vnodeop_p, ntfs_vnodeop_entries }; + +VNODEOP_SET(ntfs_vnodeop_opv_desc); + +#elif defined(__OpenBSD__) +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 }, + { &vop_pathconf_desc, ntfs_pathconf }, + + { &vop_islocked_desc, (vop_t *)vop_generic_islocked }, + { &vop_unlock_desc, (vop_t *)vop_generic_unlock }, + { &vop_lock_desc, (vop_t *)vop_generic_lock }, + { &vop_lookup_desc, (vop_t *)ntfs_lookup }, + + { &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_strategy_desc, (vop_t *)ntfs_strategy }, + { &vop_bwrite_desc, (vop_t *)vop_generic_bwrite }, + { &vop_read_desc, (vop_t *)ntfs_read }, + { &vop_write_desc, (vop_t *)ntfs_write }, + + { NULL, NULL } +}; + +const struct vnodeopv_desc ntfs_vnodeop_opv_desc = + { &ntfs_vnodeop_p, ntfs_vnodeop_entries }; + +#else /* !FreeBSD && !OpenBSD*/ + +const struct vnodeopv_entry_desc ntfs_vnodeop_entries[] = { + { &vop_default_desc, (vop_t *) ntfs_bypass }, + { &vop_lookup_desc, (vop_t *) ntfs_lookup }, /* lookup */ + { &vop_create_desc, genfs_eopnotsupp }, /* create */ + { &vop_mknod_desc, genfs_eopnotsupp }, /* mknod */ + { &vop_open_desc, (vop_t *) ntfs_open }, /* open */ + { &vop_close_desc,(vop_t *) ntfs_close }, /* close */ + { &vop_access_desc, (vop_t *) ntfs_access }, /* access */ + { &vop_getattr_desc, (vop_t *) ntfs_getattr }, /* getattr */ + { &vop_setattr_desc, genfs_eopnotsupp }, /* setattr */ + { &vop_read_desc, (vop_t *) ntfs_read }, /* read */ + { &vop_write_desc, (vop_t *) ntfs_write }, /* write */ + { &vop_lease_desc, genfs_lease_check }, /* lease */ + { &vop_fcntl_desc, genfs_fcntl }, /* fcntl */ + { &vop_ioctl_desc, genfs_enoioctl }, /* ioctl */ + { &vop_poll_desc, genfs_poll }, /* poll */ + { &vop_kqfilter_desc, genfs_kqfilter }, /* kqfilter */ + { &vop_revoke_desc, genfs_revoke }, /* revoke */ + { &vop_mmap_desc, genfs_mmap }, /* mmap */ + { &vop_fsync_desc, genfs_fsync }, /* fsync */ + { &vop_seek_desc, genfs_seek }, /* seek */ + { &vop_remove_desc, genfs_eopnotsupp }, /* remove */ + { &vop_link_desc, genfs_eopnotsupp }, /* link */ + { &vop_rename_desc, genfs_eopnotsupp }, /* rename */ + { &vop_mkdir_desc, genfs_eopnotsupp }, /* mkdir */ + { &vop_rmdir_desc, genfs_eopnotsupp }, /* rmdir */ + { &vop_symlink_desc, genfs_eopnotsupp }, /* symlink */ + { &vop_readdir_desc, (vop_t *) ntfs_readdir }, /* readdir */ + { &vop_readlink_desc, genfs_eopnotsupp }, /* readlink */ + { &vop_abortop_desc, genfs_abortop }, /* abortop */ + { &vop_inactive_desc, (vop_t *) ntfs_inactive }, /* inactive */ + { &vop_reclaim_desc, (vop_t *) ntfs_reclaim }, /* reclaim */ + { &vop_lock_desc, genfs_lock }, /* lock */ + { &vop_unlock_desc, genfs_unlock }, /* unlock */ + { &vop_bmap_desc, (vop_t *) ntfs_bmap }, /* bmap */ + { &vop_strategy_desc, (vop_t *) ntfs_strategy }, /* strategy */ + { &vop_print_desc, (vop_t *) ntfs_print }, /* print */ + { &vop_islocked_desc, genfs_islocked }, /* islocked */ + { &vop_pathconf_desc, ntfs_pathconf }, /* pathconf */ + { &vop_advlock_desc, genfs_nullop }, /* advlock */ + { &vop_blkatoff_desc, genfs_eopnotsupp }, /* blkatoff */ + { &vop_valloc_desc, genfs_eopnotsupp }, /* valloc */ + { &vop_reallocblks_desc, genfs_eopnotsupp }, /* reallocblks */ + { &vop_vfree_desc, genfs_eopnotsupp }, /* vfree */ + { &vop_truncate_desc, genfs_eopnotsupp }, /* truncate */ + { &vop_update_desc, genfs_eopnotsupp }, /* update */ + { &vop_bwrite_desc, vn_bwrite }, /* bwrite */ + { &vop_getpages_desc, genfs_compat_getpages }, /* getpages */ + { &vop_putpages_desc, genfs_putpages }, /* putpages */ + { NULL, NULL } +}; +const struct vnodeopv_desc ntfs_vnodeop_opv_desc = + { &ntfs_vnodeop_p, ntfs_vnodeop_entries }; + +#endif diff --git a/sys/ntfs/ntfsmount.h b/sys/ntfs/ntfsmount.h new file mode 100644 index 00000000000..14f4f25f1d4 --- /dev/null +++ b/sys/ntfs/ntfsmount.h @@ -0,0 +1,36 @@ +/* $Id: ntfsmount.h,v 1.1 2003/05/20 03:03:27 tedu Exp $ */ +/* $NetBSD: ntfsmount.h,v 1.1 2002/12/23 17:38:34 jdolecek Exp $ */ + +/*- + * 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.4 1999/05/12 09:43:09 semenu Exp + */ + +#define NTFS_MFLAG_CASEINS 0x00000001 +#define NTFS_MFLAG_ALLNAMES 0x00000002 + +#define NTFS_MFLAG_BITS "\177\20" \ + "b\00caseins\0b\01allnames" |