diff options
author | bp <bp@FreeBSD.org> | 1999-10-02 04:06:24 +0000 |
---|---|---|
committer | bp <bp@FreeBSD.org> | 1999-10-02 04:06:24 +0000 |
commit | 99edac4b3b7b369e65394b2bd57332aaad8d6812 (patch) | |
tree | 648f799b08d76bc3345446a922bb7985b440fae9 /sys/fs/nwfs/nwfs_subr.c | |
parent | 4f9461c0bd8dd6d2fb8a4f0df36c1666e4e25c98 (diff) | |
download | FreeBSD-src-99edac4b3b7b369e65394b2bd57332aaad8d6812.zip FreeBSD-src-99edac4b3b7b369e65394b2bd57332aaad8d6812.tar.gz |
Import kernel part of ncplib: netncp and nwfs
Reviewed by: msmith, peter
Obtained from: ncplib
Diffstat (limited to 'sys/fs/nwfs/nwfs_subr.c')
-rw-r--r-- | sys/fs/nwfs/nwfs_subr.c | 669 |
1 files changed, 669 insertions, 0 deletions
diff --git a/sys/fs/nwfs/nwfs_subr.c b/sys/fs/nwfs/nwfs_subr.c new file mode 100644 index 0000000..d57d675 --- /dev/null +++ b/sys/fs/nwfs/nwfs_subr.c @@ -0,0 +1,669 @@ +/* + * Copyright (c) 1999, 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/proc.h> +#include <sys/malloc.h> +#include <machine/clock.h> +#include <sys/time.h> +#include <sys/namei.h> + +#include <netncp/ncp.h> +#include <netncp/ncp_conn.h> +#include <netncp/ncp_ncp.h> +#include <netncp/ncp_subr.h> +#include <netncp/ncp_user.h> +#include <netncp/ncp_rq.h> +#include <netncp/nwerror.h> + +#include <nwfs/nwfs.h> +#include <nwfs/nwfs_node.h> +#include <nwfs/nwfs_subr.h> + +MALLOC_DEFINE(M_NWFSDATA, "NWFS data", "NWFS private data"); + +static void +ncp_extract_file_info(struct nwmount *nmp, struct ncp_rq *rqp, struct nw_entry_info *target) { + u_char name_len; + const int info_struct_size = sizeof(struct nw_entry_info) - 257; + + ncp_rp_mem(rqp,(caddr_t)target,info_struct_size); + name_len = ncp_rp_byte(rqp); + target->nameLen = name_len; + ncp_rp_mem(rqp,(caddr_t)target->entryName, name_len); + target->entryName[name_len] = '\0'; + ncp_path2unix(target->entryName, target->entryName, name_len, &nmp->m.nls); + return; +} + +static void +ncp_update_file_info(struct nwmount *nmp, struct ncp_rq *rqp, + struct nw_entry_info *target) +{ + int info_struct_size = sizeof(struct nw_entry_info) - 257; + + ncp_rp_mem(rqp,(caddr_t)target,info_struct_size); + return; +} + +int +ncp_initsearch(struct vnode *dvp,struct proc *p,struct ucred *cred) +{ + struct nwmount *nmp = VTONWFS(dvp); + struct ncp_conn *conn = NWFSTOCONN(nmp); + struct nwnode *np = VTONW(dvp); + u_int8_t volnum = nmp->n_volume; + u_int32_t dirent = np->n_fid.f_id; + int error; + DECLARE_RQ; + + NCPNDEBUG("vol=%d,dir=%d\n", volnum, dirent); + NCP_RQ_HEAD(87,p,cred); + ncp_rq_byte(rqp, 2); /* subfunction */ + ncp_rq_byte(rqp, nmp->name_space); + ncp_rq_byte(rqp, 0); /* reserved */ + ncp_rq_dbase_path(rqp, volnum, dirent, 0, NULL, NULL); + checkbad(ncp_request(conn,rqp)); + ncp_rp_mem(rqp,(caddr_t)&np->n_seq, sizeof(np->n_seq)); + NCP_RQ_EXIT; + return error; +} + +int +ncp_search_for_file_or_subdir(struct nwmount *nmp, + struct nw_search_seq *seq, + struct nw_entry_info *target, + struct proc *p,struct ucred *cred) +{ + struct ncp_conn *conn = NWFSTOCONN(nmp); + int error; + DECLARE_RQ; + + NCP_RQ_HEAD(87,p,cred); + ncp_rq_byte(rqp, 3); /* subfunction */ + ncp_rq_byte(rqp, nmp->name_space); + ncp_rq_byte(rqp, 0); /* data stream */ + ncp_rq_word_lh(rqp, 0xffff); /* Search attribs */ + ncp_rq_dword(rqp, IM_ALL); /* return info mask */ + ncp_rq_mem(rqp, (caddr_t)seq, 9); + ncp_rq_byte(rqp, 2); /* 2 byte pattern */ + ncp_rq_byte(rqp, 0xff); /* following is a wildcard */ + ncp_rq_byte(rqp, '*'); + checkbad(ncp_request(conn,rqp)); + ncp_rp_mem(rqp,(caddr_t)seq, sizeof(*seq)); + ncp_rp_byte(rqp); /* skip */ + ncp_extract_file_info(nmp, rqp, target); + NCP_RQ_EXIT; + return error; +} + +/* + * Returns information for a (one-component) name relative to the specified + * directory. + */ +int +ncp_obtain_info(struct nwmount *nmp, u_int32_t dirent, + int namelen, char *path, struct nw_entry_info *target, + struct proc *p,struct ucred *cred) +{ + struct ncp_conn *conn=NWFSTOCONN(nmp); + int error; + u_char volnum = nmp->n_volume, ns; + DECLARE_RQ; + + if (target == NULL) { + NCPFATAL("target == NULL\n"); + return EINVAL; + } + ns = (path == NULL || path[0] == 0) ? NW_NS_DOS : nmp->name_space; + NCP_RQ_HEAD(87, p, cred); + ncp_rq_byte(rqp, 6); /* subfunction */ + ncp_rq_byte(rqp, ns); + ncp_rq_byte(rqp, ns); /* DestNameSpace */ + ncp_rq_word(rqp, htons(0xff00)); /* get all */ + ncp_rq_dword(rqp, IM_ALL); + ncp_rq_dbase_path(rqp, volnum, dirent, namelen, path, &nmp->m.nls); + checkbad(ncp_request(conn,rqp)); + if (path) + ncp_extract_file_info(nmp, rqp, target); + else + ncp_update_file_info(nmp, rqp, target); + NCP_RQ_EXIT; + return error; +} +/* + * lookup name pointed by cnp in directory dvp and return file info in np. + * May be I should create a little cache, but another way is to minimize + * number of calls, on other hand, in multiprocess environment ... + */ +int +ncp_lookup(struct vnode *dvp, int len, char *name, struct nw_entry_info *fap, + struct proc *p,struct ucred *cred) +{ + struct nwmount *nmp; + struct nwnode *dnp = VTONW(dvp); + struct ncp_conn *conn; + int error; + + if (!dvp || dvp->v_type != VDIR) { + nwfs_printf("dvp is NULL or not a directory.\n"); + return (ENOENT); + } + nmp = VTONWFS(dvp); + conn = NWFSTOCONN(nmp); + + if (len == 1 && name[0] == '.') { + if (strcmp(dnp->n_name, NWFS_ROOTVOL) == 0) { + error = ncp_obtain_info(nmp, dnp->n_fid.f_id, 0, NULL, + fap, p, cred); + } else { + error = ncp_obtain_info(nmp, dnp->n_fid.f_parent, + dnp->n_nmlen, dnp->n_name, fap, p, cred); + } + return error; + } else if (len == 2 && name[0] == '.' && name[1] == '.') { + printf("%s: knows NOTHING about '..'\n", __FUNCTION__); + return EIO; + } else { + error = ncp_obtain_info(nmp, dnp->n_fid.f_id, + len, name, fap, p, cred); + } + return error; +} + +static void ConvertToNWfromDWORD(u_int32_t sfd, ncp_fh *fh); +static void +ConvertToNWfromDWORD(u_int32_t sfd, ncp_fh *fh) { + fh->val1 = (fh->val.val32 = sfd); + return; +} + +/* + * If both dir and name are NULL, then in target there's already a looked-up + * entry that wants to be opened. + */ +int +ncp_open_create_file_or_subdir(struct nwmount *nmp,struct vnode *dvp,int namelen, + char *name, int open_create_mode, u_int32_t create_attributes, + int desired_acc_rights, struct ncp_open_info *nop, + struct proc *p,struct ucred *cred) +{ + + struct ncp_conn *conn=NWFSTOCONN(nmp); + u_int16_t search_attribs = SA_ALL & (~SA_SUBDIR_FILES); + u_int8_t volnum; + u_int32_t dirent; + int error; + DECLARE_RQ; + + volnum = nmp->n_volume; + dirent = VTONW(dvp)->n_fid.f_id; + if ((create_attributes & aDIR) != 0) { + search_attribs |= SA_SUBDIR_FILES; + } + NCP_RQ_HEAD(87,p,cred); + ncp_rq_byte(rqp, 1);/* subfunction */ + ncp_rq_byte(rqp, nmp->name_space); + ncp_rq_byte(rqp, open_create_mode); + ncp_rq_word(rqp, search_attribs); + ncp_rq_dword(rqp, IM_ALL); + ncp_rq_dword(rqp, create_attributes); + /* + * The desired acc rights seem to be the inherited rights mask for + * directories + */ + ncp_rq_word(rqp, desired_acc_rights); + ncp_rq_dbase_path(rqp, volnum, dirent, namelen, name, &nmp->m.nls); + checkbad(ncp_request(conn,rqp)); + + nop->origfh = ncp_rp_dword_lh(rqp); + nop->action = ncp_rp_byte(rqp); + ncp_rp_byte(rqp); /* skip */ + ncp_extract_file_info(nmp, rqp, &nop->fattr); + ConvertToNWfromDWORD(nop->origfh, &nop->fh); + NCP_RQ_EXIT; + switch(error) { + case NWE_FILE_NO_CREATE_PRIV: + error = EACCES; + break; + } + return error; +} + +int +ncp_close_file(struct ncp_conn *conn, ncp_fh *fh,struct proc *p,struct ucred *cred) { + int error; + DECLARE_RQ; + + NCP_RQ_HEAD(66,p,cred); + ncp_rq_byte(rqp, 0); + ncp_rq_mem(rqp, (caddr_t)fh, 6); + error = ncp_request(conn,rqp); + NCP_RQ_EXIT_NB; + return error; +} + +int +ncp_DeleteNSEntry(struct nwmount *nmp, u_int32_t dirent, + int namelen,char *name,struct proc *p,struct ucred *cred) +{ + int error; + struct ncp_conn *conn=NWFSTOCONN(nmp); + DECLARE_RQ; + + NCP_RQ_HEAD(87,p,cred); + ncp_rq_byte(rqp, 8); /* subfunction */ + ncp_rq_byte(rqp, nmp->name_space); + ncp_rq_byte(rqp, 0); /* reserved */ + ncp_rq_word(rqp, SA_ALL); /* search attribs: all */ + ncp_rq_dbase_path(rqp, nmp->n_volume, dirent, namelen, name, &nmp->m.nls); + error = ncp_request(conn,rqp); + NCP_RQ_EXIT_NB; + return error; +} + +int +ncp_nsrename(struct ncp_conn *conn, int volume, int ns, int oldtype, + struct ncp_nlstables *nt, + nwdirent fdir, char *old_name, int oldlen, + nwdirent tdir, char *new_name, int newlen, + struct proc *p, struct ucred *cred) +{ + DECLARE_RQ; + int error; + + NCP_RQ_HEAD(87,p,cred); + ncp_rq_byte(rqp, 4); + ncp_rq_byte(rqp, ns); + ncp_rq_byte(rqp, 1); + ncp_rq_word(rqp, oldtype); + /* source Handle Path */ + ncp_rq_byte(rqp, volume); + ncp_rq_dword(rqp, fdir); + ncp_rq_byte(rqp, 1); + ncp_rq_byte(rqp, 1); /* 1 source component */ + /* dest Handle Path */ + ncp_rq_byte(rqp, volume); + ncp_rq_dword(rqp, tdir); + ncp_rq_byte(rqp, 1); + ncp_rq_byte(rqp, 1); /* 1 destination component */ + ncp_rq_pathstring(rqp, oldlen, old_name, nt); + ncp_rq_pathstring(rqp, newlen, new_name, nt); + error = ncp_request(conn,rqp); + NCP_RQ_EXIT_NB; + return error; +} + +int +ncp_modify_file_or_subdir_dos_info(struct nwmount *nmp, struct vnode *vp, + u_int32_t info_mask, + struct nw_modify_dos_info *info, + struct proc *p,struct ucred *cred) +{ + struct nwnode *np=VTONW(vp); + u_int8_t volnum = nmp->n_volume; + u_int32_t dirent = np->n_fid.f_id; + struct ncp_conn *conn=NWFSTOCONN(nmp); + int error; + DECLARE_RQ; + + NCP_RQ_HEAD(87,p,cred); + ncp_rq_byte(rqp, 7); /* subfunction */ + ncp_rq_byte(rqp, nmp->name_space); + ncp_rq_byte(rqp, 0); /* reserved */ + ncp_rq_word(rqp, htons(0x0680)); /* search attribs: all */ + ncp_rq_dword(rqp, info_mask); + ncp_rq_mem(rqp, (caddr_t)info, sizeof(*info)); + ncp_rq_dbase_path(rqp, volnum, dirent, 0, NULL, NULL); + error = ncp_request(conn,rqp); + NCP_RQ_EXIT_NB; + return error; +} + +int +ncp_setattr(vp, vap, cred, procp) + register struct vnode *vp; + register struct vattr *vap; + struct ucred *cred; + struct proc *procp; +{ + struct nwmount *nmp=VTONWFS(vp); + struct nwnode *np=VTONW(vp); + struct ncp_open_info nwn; + struct ncp_conn *conn=NWFSTOCONN(nmp); + struct nw_modify_dos_info info; + int error = 0, info_mask; + DECLARE_RQ; + + if (vap->va_size != VNOVAL) { + error = ncp_open_create_file_or_subdir(nmp, vp, 0, NULL, OC_MODE_OPEN, 0, + AR_WRITE | AR_READ, &nwn,procp,cred); + if (error) return error; + NCP_RQ_HEAD(73,procp,cred); + ncp_rq_byte(rqp, 0); + ncp_rq_mem(rqp, (caddr_t)&nwn.fh, 6); + ncp_rq_dword(rqp, htonl(vap->va_size)); + ncp_rq_word_hl(rqp, 0); + checkbad(ncp_request(conn,rqp)); + np->n_vattr.va_size = np->n_size = vap->va_size; + NCP_RQ_EXIT; + ncp_close_file(conn, &nwn.fh, procp, cred); + if (error) return error; + } + info_mask = 0; + bzero(&info, sizeof(info)); + + if (vap->va_mtime.tv_sec != VNOVAL) { + info_mask |= (DM_MODIFY_TIME | DM_MODIFY_DATE); + ncp_unix2dostime(&vap->va_mtime, &info.modifyDate, &info.modifyTime, NULL); + } + if (vap->va_atime.tv_sec != VNOVAL) { + info_mask |= (DM_LAST_ACCESS_DATE); + ncp_unix2dostime(&vap->va_atime, &info.lastAccessDate,NULL,NULL); + } + if (info_mask) { + error = ncp_modify_file_or_subdir_dos_info(nmp, vp, info_mask, &info,procp,cred); + } + return (error); +} + +int +ncp_get_volume_info_with_number(struct ncp_conn *conn, + int n, struct ncp_volume_info *target, + struct proc *p,struct ucred *cred) { + int error,len; + DECLARE_RQ; + + NCP_RQ_HEAD_S(22,44,p,cred); + ncp_rq_byte(rqp,n); + checkbad(ncp_request(conn,rqp)); + target->total_blocks = ncp_rp_dword_lh(rqp); + target->free_blocks = ncp_rp_dword_lh(rqp); + target->purgeable_blocks = ncp_rp_dword_lh(rqp); + target->not_yet_purgeable_blocks = ncp_rp_dword_lh(rqp); + target->total_dir_entries = ncp_rp_dword_lh(rqp); + target->available_dir_entries = ncp_rp_dword_lh(rqp); + ncp_rp_dword_lh(rqp); + target->sectors_per_block = ncp_rp_byte(rqp); + bzero(&target->volume_name, sizeof(target->volume_name)); + len = ncp_rp_byte(rqp); + if (len > NCP_VOLNAME_LEN) { + error = ENAMETOOLONG; + } else { + ncp_rp_mem(rqp,(caddr_t)&target->volume_name, len); + } + NCP_RQ_EXIT; + return error; +} + +int +ncp_get_namespaces(struct ncp_conn *conn, u_int32_t volume, int *nsf, + struct proc *p,struct ucred *cred) { + int error; + u_int8_t ns; + u_int16_t nscnt; + DECLARE_RQ; + + NCP_RQ_HEAD(87,p,cred); + ncp_rq_byte(rqp, 24); /* Subfunction: Get Loaded Name Spaces */ + ncp_rq_word(rqp, 0); + ncp_rq_byte(rqp, volume); + checkbad(ncp_request(conn,rqp)); + nscnt = ncp_rp_word_lh(rqp); + *nsf = 0; + while (nscnt-- > 0) { + ns = ncp_rp_byte(rqp); + *nsf |= 1 << ns; + } + NCP_RQ_EXIT; + return error; +} + +int +ncp_lookup_volume(struct ncp_conn *conn, char *volname, + u_char *volNum, u_int32_t *dirEnt, + struct proc *p,struct ucred *cred) +{ + int error; + DECLARE_RQ; + + NCPNDEBUG("looking up vol %s\n", volname); + NCP_RQ_HEAD(87,p,cred); + ncp_rq_byte(rqp, 22); /* Subfunction: Generate dir handle */ + ncp_rq_byte(rqp, 0); /* src name space */ + ncp_rq_byte(rqp, 0); /* dst name space, always zero */ + ncp_rq_word(rqp, 0); /* dstNSIndicator */ + + ncp_rq_byte(rqp, 0); /* faked volume number */ + ncp_rq_dword(rqp, 0); /* faked dir_base */ + ncp_rq_byte(rqp, 0xff); /* Don't have a dir_base */ + ncp_rq_byte(rqp, 1); /* 1 path component */ + ncp_rq_pstring(rqp, volname); + checkbad(ncp_request(conn,rqp)); + ncp_rp_dword_lh(rqp); /* NSDirectoryBase*/ + *dirEnt = ncp_rp_dword_lh(rqp); + *volNum = ncp_rp_byte(rqp); + NCP_RQ_EXIT; + return error; +} + +/* + * 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; +/* + * Convert the unix version of time to dos's idea of time to be used in + * file timestamps. The passed in unix time is assumed to be in GMT. + */ +void +ncp_unix2dostime(tsp, ddp, dtp, dhp) + struct timespec *tsp; + u_int16_t *ddp; + u_int16_t *dtp; + u_int8_t *dhp; +{ + u_long t; + u_long days; + u_long inc; + u_long year; + u_long month; + u_short *months; + + /* + * If the time from the last conversion is the same as now, then + * skip the computations and use the saved result. + */ + t = tsp->tv_sec - (tz.tz_minuteswest * 60) + - (wall_cmos_clock ? adjkerntz : 0); + /* - daylight savings time correction */ + 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; + +/* + * Convert from dos' idea of time to unix'. This will probably only be + * called from the stat(), and fstat() system calls and so probably need + * not be too efficient. + */ +void +ncp_dos2unixtime(dd, dt, dh, tsp) + u_int dd; + u_int dt; + u_int dh; + struct timespec *tsp; +{ + u_long seconds; + u_long month; + u_long year; + u_long days; + u_short *months; + + if (dd == 0) { + /* + * Uninitialized field, return the epoch. + */ + 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; + } + tsp->tv_sec = seconds + lastseconds + (tz.tz_minuteswest * 60) + + adjkerntz; + /* + daylight savings time correction */ + tsp->tv_nsec = (dh % 100) * 10000000; +} |