diff options
Diffstat (limited to 'sys/fs/smbfs/smbfs_subr.c')
-rw-r--r-- | sys/fs/smbfs/smbfs_subr.c | 326 |
1 files changed, 326 insertions, 0 deletions
diff --git a/sys/fs/smbfs/smbfs_subr.c b/sys/fs/smbfs/smbfs_subr.c new file mode 100644 index 0000000..8012c0b --- /dev/null +++ b/sys/fs/smbfs/smbfs_subr.c @@ -0,0 +1,326 @@ +/* + * Copyright (c) 2000-2001, Boris Popov + * 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 Boris Popov. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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. + * + * $FreeBSD$ + */ +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <machine/clock.h> +#include <sys/time.h> +#include <sys/vnode.h> +#include <sys/sysctl.h> +#include <sys/iconv.h> + +#include <netsmb/smb.h> +#include <netsmb/smb_conn.h> +#include <netsmb/smb_subr.h> +#include <netsmb/smb_rq.h> +#include <netsmb/smb_dev.h> + +#include <fs/smbfs/smbfs.h> +#include <fs/smbfs/smbfs_node.h> +#include <fs/smbfs/smbfs_subr.h> + +MALLOC_DEFINE(M_SMBFSDATA, "SMBFS data", "SMBFS private data"); + +/* + * Time & date conversion routines taken from msdosfs. Although leap + * year calculation is bogus, it's sufficient before 2100 :) + */ +/* + * This is the format of the contents of the deTime field in the direntry + * structure. + * We don't use bitfields because we don't know how compilers for + * arbitrary machines will lay them out. + */ +#define DT_2SECONDS_MASK 0x1F /* seconds divided by 2 */ +#define DT_2SECONDS_SHIFT 0 +#define DT_MINUTES_MASK 0x7E0 /* minutes */ +#define DT_MINUTES_SHIFT 5 +#define DT_HOURS_MASK 0xF800 /* hours */ +#define DT_HOURS_SHIFT 11 + +/* + * This is the format of the contents of the deDate field in the direntry + * structure. + */ +#define DD_DAY_MASK 0x1F /* day of month */ +#define DD_DAY_SHIFT 0 +#define DD_MONTH_MASK 0x1E0 /* month */ +#define DD_MONTH_SHIFT 5 +#define DD_YEAR_MASK 0xFE00 /* year - 1980 */ +#define DD_YEAR_SHIFT 9 +/* + * Total number of days that have passed for each month in a regular year. + */ +static u_short regyear[] = { + 31, 59, 90, 120, 151, 181, + 212, 243, 273, 304, 334, 365 +}; + +/* + * Total number of days that have passed for each month in a leap year. + */ +static u_short leapyear[] = { + 31, 60, 91, 121, 152, 182, + 213, 244, 274, 305, 335, 366 +}; + +/* + * Variables used to remember parts of the last time conversion. Maybe we + * can avoid a full conversion. + */ +static u_long lasttime; +static u_long lastday; +static u_short lastddate; +static u_short lastdtime; + +void +smb_time_local2server(struct timespec *tsp, int tzoff, u_long *seconds) +{ + *seconds = tsp->tv_sec - tzoff * 60 /*- tz.tz_minuteswest * 60 - + (wall_cmos_clock ? adjkerntz : 0)*/; +} + +void +smb_time_server2local(u_long seconds, int tzoff, struct timespec *tsp) +{ + tsp->tv_sec = seconds + tzoff * 60; + /*+ tz.tz_minuteswest * 60 + (wall_cmos_clock ? adjkerntz : 0)*/; +} + +/* + * Number of seconds between 1970 and 1601 year + */ +int64_t DIFF1970TO1601 = 11644473600ULL; + +/* + * Time from server comes as UTC, so no need to use tz + */ +void +smb_time_NT2local(int64_t nsec, int tzoff, struct timespec *tsp) +{ + smb_time_server2local(nsec / 10000000 - DIFF1970TO1601, 0, tsp); +} + +void +smb_time_local2NT(struct timespec *tsp, int tzoff, int64_t *nsec) +{ + u_long seconds; + + smb_time_local2server(tsp, 0, &seconds); + *nsec = (((int64_t)(seconds) & ~1) + DIFF1970TO1601) * (int64_t)10000000; +} + +void +smb_time_unix2dos(struct timespec *tsp, int tzoff, u_int16_t *ddp, + u_int16_t *dtp, u_int8_t *dhp) +{ + u_long t, days, year, month, inc; + u_short *months; + + /* + * If the time from the last conversion is the same as now, then + * skip the computations and use the saved result. + */ + smb_time_local2server(tsp, tzoff, &t); + t &= ~1; + if (lasttime != t) { + lasttime = t; + lastdtime = (((t / 2) % 30) << DT_2SECONDS_SHIFT) + + (((t / 60) % 60) << DT_MINUTES_SHIFT) + + (((t / 3600) % 24) << DT_HOURS_SHIFT); + + /* + * If the number of days since 1970 is the same as the last + * time we did the computation then skip all this leap year + * and month stuff. + */ + days = t / (24 * 60 * 60); + if (days != lastday) { + lastday = days; + for (year = 1970;; year++) { + inc = year & 0x03 ? 365 : 366; + if (days < inc) + break; + days -= inc; + } + months = year & 0x03 ? regyear : leapyear; + for (month = 0; days >= months[month]; month++) + ; + if (month > 0) + days -= months[month - 1]; + lastddate = ((days + 1) << DD_DAY_SHIFT) + + ((month + 1) << DD_MONTH_SHIFT); + /* + * Remember dos's idea of time is relative to 1980. + * unix's is relative to 1970. If somehow we get a + * time before 1980 then don't give totally crazy + * results. + */ + if (year > 1980) + lastddate += (year - 1980) << DD_YEAR_SHIFT; + } + } + if (dtp) + *dtp = lastdtime; + if (dhp) + *dhp = (tsp->tv_sec & 1) * 100 + tsp->tv_nsec / 10000000; + + *ddp = lastddate; +} + +/* + * The number of seconds between Jan 1, 1970 and Jan 1, 1980. In that + * interval there were 8 regular years and 2 leap years. + */ +#define SECONDSTO1980 (((8 * 365) + (2 * 366)) * (24 * 60 * 60)) + +static u_short lastdosdate; +static u_long lastseconds; + +void +smb_dos2unixtime(u_int dd, u_int dt, u_int dh, int tzoff, + struct timespec *tsp) +{ + u_long seconds; + u_long month; + u_long year; + u_long days; + u_short *months; + + if (dd == 0) { + tsp->tv_sec = 0; + tsp->tv_nsec = 0; + return; + } + seconds = (((dt & DT_2SECONDS_MASK) >> DT_2SECONDS_SHIFT) << 1) + + ((dt & DT_MINUTES_MASK) >> DT_MINUTES_SHIFT) * 60 + + ((dt & DT_HOURS_MASK) >> DT_HOURS_SHIFT) * 3600 + + dh / 100; + /* + * If the year, month, and day from the last conversion are the + * same then use the saved value. + */ + if (lastdosdate != dd) { + lastdosdate = dd; + days = 0; + year = (dd & DD_YEAR_MASK) >> DD_YEAR_SHIFT; + days = year * 365; + days += year / 4 + 1; /* add in leap days */ + if ((year & 0x03) == 0) + days--; /* if year is a leap year */ + months = year & 0x03 ? regyear : leapyear; + month = (dd & DD_MONTH_MASK) >> DD_MONTH_SHIFT; + if (month < 1 || month > 12) { + month = 1; + } + if (month > 1) + days += months[month - 2]; + days += ((dd & DD_DAY_MASK) >> DD_DAY_SHIFT) - 1; + lastseconds = (days * 24 * 60 * 60) + SECONDSTO1980; + } + smb_time_server2local(seconds + lastseconds, tzoff, tsp); + tsp->tv_nsec = (dh % 100) * 10000000; +} + +static int +smb_fphelp(struct mbchain *mbp, struct smb_vc *vcp, struct smbnode *np, + int caseopt) +{ + struct smbmount *smp= np->n_mount; + struct smbnode **npp = smp->sm_npstack; + int i, error = 0; + +/* simple_lock(&smp->sm_npslock);*/ + i = 0; + while (np->n_parent) { + if (i++ == SMBFS_MAXPATHCOMP) { +/* simple_unlock(&smp->sm_npslock);*/ + return ENAMETOOLONG; + } + *npp++ = np; + np = np->n_parent; + } +/* if (i == 0) + return smb_put_dmem(mbp, vcp, "\\", 2, caseopt);*/ + while (i--) { + np = *--npp; + error = mb_put_uint8(mbp, '\\'); + if (error) + break; + error = smb_put_dmem(mbp, vcp, np->n_name, np->n_nmlen, caseopt); + if (error) + break; + } +/* simple_unlock(&smp->sm_npslock);*/ + return error; +} + +int +smbfs_fullpath(struct mbchain *mbp, struct smb_vc *vcp, struct smbnode *dnp, + const char *name, int nmlen) +{ + int caseopt = SMB_CS_NONE; + int error; + + if (SMB_DIALECT(vcp) < SMB_DIALECT_LANMAN1_0) + caseopt |= SMB_CS_UPPER; + if (dnp != NULL) { + error = smb_fphelp(mbp, vcp, dnp, caseopt); + if (error) + return error; + } + if (name) { + error = mb_put_uint8(mbp, '\\'); + if (error) + return error; + error = smb_put_dmem(mbp, vcp, name, nmlen, caseopt); + if (error) + return error; + } + error = mb_put_uint8(mbp, 0); + return error; +} + +int +smbfs_fname_tolocal(struct smb_vc *vcp, char *name, int nmlen, int caseopt) +{ +/* if (caseopt & SMB_CS_UPPER) + iconv_convmem(vcp->vc_toupper, name, name, nmlen); + else if (caseopt & SMB_CS_LOWER) + iconv_convmem(vcp->vc_tolower, name, name, nmlen);*/ + if (vcp->vc_tolocal) + iconv_convmem(vcp->vc_tolocal, name, name, nmlen); + return 0; +} |