summaryrefslogtreecommitdiffstats
path: root/sys/nwfs/nwfs_subr.c
diff options
context:
space:
mode:
authorbp <bp@FreeBSD.org>1999-10-02 04:06:24 +0000
committerbp <bp@FreeBSD.org>1999-10-02 04:06:24 +0000
commit99edac4b3b7b369e65394b2bd57332aaad8d6812 (patch)
tree648f799b08d76bc3345446a922bb7985b440fae9 /sys/nwfs/nwfs_subr.c
parent4f9461c0bd8dd6d2fb8a4f0df36c1666e4e25c98 (diff)
downloadFreeBSD-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/nwfs/nwfs_subr.c')
-rw-r--r--sys/nwfs/nwfs_subr.c669
1 files changed, 669 insertions, 0 deletions
diff --git a/sys/nwfs/nwfs_subr.c b/sys/nwfs/nwfs_subr.c
new file mode 100644
index 0000000..d57d675
--- /dev/null
+++ b/sys/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;
+}
OpenPOWER on IntegriCloud