Add the DragonFly cvs id and perform general cleanups on cvs/rcs/sccs ids. Most
[dragonfly.git] / sys / vfs / nwfs / nwfs_subr.c
1 /*
2  * Copyright (c) 1999, Boris Popov
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *    This product includes software developed by Boris Popov.
16  * 4. Neither the name of the author nor the names of any co-contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  * $FreeBSD: src/sys/nwfs/nwfs_subr.c,v 1.2.2.2 2000/10/25 02:11:10 bp Exp $
33  * $DragonFly: src/sys/vfs/nwfs/nwfs_subr.c,v 1.2 2003/06/17 04:28:54 dillon Exp $
34  */
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/kernel.h>
38 #include <sys/malloc.h>
39 #include <machine/clock.h>
40 #include <sys/time.h>
41
42 #include <netncp/ncp.h>
43 #include <netncp/ncp_conn.h>
44 #include <netncp/ncp_ncp.h>
45 #include <netncp/ncp_subr.h>
46 #include <netncp/ncp_rq.h>
47 #include <netncp/nwerror.h>
48
49 #include <nwfs/nwfs.h>
50 #include <nwfs/nwfs_node.h>
51 #include <nwfs/nwfs_subr.h>
52
53 MALLOC_DEFINE(M_NWFSDATA, "NWFS data", "NWFS private data");
54
55 static void 
56 ncp_extract_file_info(struct nwmount *nmp, struct ncp_rq *rqp, struct nw_entry_info *target) {
57         u_char name_len;
58         const int info_struct_size = sizeof(struct nw_entry_info) - 257;
59
60         ncp_rp_mem(rqp,(caddr_t)target,info_struct_size);
61         name_len = ncp_rp_byte(rqp);
62         target->nameLen = name_len;
63         ncp_rp_mem(rqp,(caddr_t)target->entryName, name_len);
64         target->entryName[name_len] = '\0';
65         ncp_path2unix(target->entryName, target->entryName, name_len, &nmp->m.nls);
66         return;
67 }
68
69 static void 
70 ncp_update_file_info(struct nwmount *nmp, struct ncp_rq *rqp, 
71         struct nw_entry_info *target)
72 {
73         int info_struct_size = sizeof(struct nw_entry_info) - 257;
74
75         ncp_rp_mem(rqp,(caddr_t)target,info_struct_size);
76         return;
77 }
78
79 int
80 ncp_initsearch(struct vnode *dvp,struct proc *p,struct ucred *cred)
81 {
82         struct nwmount *nmp = VTONWFS(dvp);
83         struct ncp_conn *conn = NWFSTOCONN(nmp);
84         struct nwnode *np = VTONW(dvp);
85         u_int8_t volnum = nmp->n_volume;
86         u_int32_t dirent = np->n_fid.f_id;
87         int error;
88         DECLARE_RQ;
89
90         NCPNDEBUG("vol=%d,dir=%d\n", volnum, dirent);
91         NCP_RQ_HEAD(87,p,cred);
92         ncp_rq_byte(rqp, 2);            /* subfunction */
93         ncp_rq_byte(rqp, nmp->name_space);
94         ncp_rq_byte(rqp, 0);            /* reserved */
95         ncp_rq_dbase_path(rqp, volnum, dirent, 0, NULL, NULL);
96         checkbad(ncp_request(conn,rqp));
97         ncp_rp_mem(rqp,(caddr_t)&np->n_seq, sizeof(np->n_seq));
98         NCP_RQ_EXIT;
99         return error;
100 }
101
102 int 
103 ncp_search_for_file_or_subdir(struct nwmount *nmp,
104                               struct nw_search_seq *seq,
105                               struct nw_entry_info *target,
106                               struct proc *p,struct ucred *cred)
107 {
108         struct ncp_conn *conn = NWFSTOCONN(nmp);
109         int error;
110         DECLARE_RQ;
111
112         NCP_RQ_HEAD(87,p,cred);
113         ncp_rq_byte(rqp, 3);            /* subfunction */
114         ncp_rq_byte(rqp, nmp->name_space);
115         ncp_rq_byte(rqp, 0);            /* data stream */
116         ncp_rq_word_lh(rqp, 0xffff);    /* Search attribs */
117         ncp_rq_dword(rqp, IM_ALL);      /* return info mask */
118         ncp_rq_mem(rqp, (caddr_t)seq, 9);
119         ncp_rq_byte(rqp, 2);            /* 2 byte pattern */
120         ncp_rq_byte(rqp, 0xff);         /* following is a wildcard */
121         ncp_rq_byte(rqp, '*');
122         checkbad(ncp_request(conn,rqp));
123         ncp_rp_mem(rqp,(caddr_t)seq, sizeof(*seq));
124         ncp_rp_byte(rqp);               /* skip */
125         ncp_extract_file_info(nmp, rqp, target);
126         NCP_RQ_EXIT;
127         return error;
128 }
129
130 /*
131  * Returns information for a (one-component) name relative to the specified
132  * directory.
133  */
134 int 
135 ncp_obtain_info(struct nwmount *nmp,  u_int32_t dirent,
136                 int namelen, char *path, struct nw_entry_info *target,
137                 struct proc *p,struct ucred *cred)
138 {
139         struct ncp_conn *conn=NWFSTOCONN(nmp);
140         int error;
141         u_char volnum = nmp->n_volume, ns;
142         DECLARE_RQ;
143
144         if (target == NULL) {
145                 NCPFATAL("target == NULL\n");
146                 return EINVAL;
147         }
148         ns = (path == NULL || path[0] == 0) ? NW_NS_DOS : nmp->name_space;
149         NCP_RQ_HEAD(87, p, cred);
150         ncp_rq_byte(rqp, 6);                    /* subfunction */
151         ncp_rq_byte(rqp, ns);
152         ncp_rq_byte(rqp, ns);   /* DestNameSpace */
153         ncp_rq_word(rqp, htons(0xff00));        /* get all */
154         ncp_rq_dword(rqp, IM_ALL);
155         ncp_rq_dbase_path(rqp, volnum, dirent, namelen, path, &nmp->m.nls);
156         checkbad(ncp_request(conn,rqp));
157         if (path)
158                 ncp_extract_file_info(nmp, rqp, target);
159         else
160                 ncp_update_file_info(nmp, rqp, target);
161         NCP_RQ_EXIT;
162         return error;
163 }
164 /* 
165  * lookup name pointed by cnp in directory dvp and return file info in np.
166  * May be I should create a little cache, but another way is to minimize
167  * number of calls, on other hand, in multiprocess environment ...
168  */
169 int
170 ncp_lookup(struct vnode *dvp, int len, char *name, struct nw_entry_info *fap,
171                 struct proc *p,struct ucred *cred)
172 {
173         struct nwmount *nmp;
174         struct nwnode *dnp = VTONW(dvp);
175         struct ncp_conn *conn;
176         int error;
177
178         if (!dvp || dvp->v_type != VDIR) {
179                 nwfs_printf("dvp is NULL or not a directory.\n");
180                 return (ENOENT);
181         }
182         nmp = VTONWFS(dvp);
183         conn = NWFSTOCONN(nmp);
184
185         if (len == 1 && name[0] == '.') {
186                 if (strcmp(dnp->n_name, NWFS_ROOTVOL) == 0) {
187                         error = ncp_obtain_info(nmp, dnp->n_fid.f_id, 0, NULL,
188                                 fap, p, cred);
189                 } else {
190                         error = ncp_obtain_info(nmp, dnp->n_fid.f_parent, 
191                                 dnp->n_nmlen, dnp->n_name, fap, p, cred);
192                 }
193                 return error;
194         } else if (len == 2 && name[0] == '.' && name[1] == '.') {
195                 printf("%s: knows NOTHING about '..'\n", __FUNCTION__);
196                 return EIO;
197         } else {
198                 error = ncp_obtain_info(nmp, dnp->n_fid.f_id, 
199                         len, name, fap, p, cred);
200         }
201         return error;
202 }
203
204 static void ConvertToNWfromDWORD(u_int32_t sfd, ncp_fh *fh);
205 static void 
206 ConvertToNWfromDWORD(u_int32_t sfd, ncp_fh *fh) {
207         fh->val1 = (fh->val.val32 = sfd);
208         return;
209 }
210
211 /*
212  * If both dir and name are NULL, then in target there's already a looked-up
213  * entry that wants to be opened.
214  */
215 int 
216 ncp_open_create_file_or_subdir(struct nwmount *nmp,struct vnode *dvp,int namelen,
217             char *name, int open_create_mode, u_int32_t create_attributes,
218             int desired_acc_rights, struct ncp_open_info *nop,
219             struct proc *p,struct ucred *cred)
220 {
221         
222         struct ncp_conn *conn=NWFSTOCONN(nmp);
223         u_int16_t search_attribs = SA_ALL & (~SA_SUBDIR_FILES);
224         u_int8_t volnum;
225         u_int32_t dirent;
226         int error;
227         DECLARE_RQ;
228
229         volnum = nmp->n_volume;
230         dirent = VTONW(dvp)->n_fid.f_id;
231         if ((create_attributes & aDIR) != 0) {
232                 search_attribs |= SA_SUBDIR_FILES;
233         }
234         NCP_RQ_HEAD(87,p,cred);
235         ncp_rq_byte(rqp, 1);/* subfunction */
236         ncp_rq_byte(rqp, nmp->name_space);
237         ncp_rq_byte(rqp, open_create_mode);
238         ncp_rq_word(rqp, search_attribs);
239         ncp_rq_dword(rqp, IM_ALL);
240         ncp_rq_dword(rqp, create_attributes);
241         /*
242          * The desired acc rights seem to be the inherited rights mask for
243          * directories
244          */
245         ncp_rq_word(rqp, desired_acc_rights);
246         ncp_rq_dbase_path(rqp, volnum, dirent, namelen, name, &nmp->m.nls);
247         checkbad(ncp_request(conn,rqp));
248
249         nop->origfh = ncp_rp_dword_lh(rqp);
250         nop->action = ncp_rp_byte(rqp);
251         ncp_rp_byte(rqp);       /* skip */
252         ncp_extract_file_info(nmp, rqp, &nop->fattr);
253         ConvertToNWfromDWORD(nop->origfh, &nop->fh);
254         NCP_RQ_EXIT;
255         switch(error) {
256             case NWE_FILE_NO_CREATE_PRIV:
257                 error = EACCES;
258                 break;
259         }
260         return error;
261 }
262
263 int
264 ncp_close_file(struct ncp_conn *conn, ncp_fh *fh,struct proc *p,struct ucred *cred) {
265         int error;
266         DECLARE_RQ;
267
268         NCP_RQ_HEAD(66,p,cred);
269         ncp_rq_byte(rqp, 0);
270         ncp_rq_mem(rqp, (caddr_t)fh, 6);
271         error = ncp_request(conn,rqp);
272         NCP_RQ_EXIT_NB;
273         return error;
274 }
275
276 int
277 ncp_DeleteNSEntry(struct nwmount *nmp, u_int32_t dirent,
278                         int namelen,char *name,struct proc *p,struct ucred *cred)
279 {
280         int error;
281         struct ncp_conn *conn=NWFSTOCONN(nmp);
282         DECLARE_RQ;
283
284         NCP_RQ_HEAD(87,p,cred);
285         ncp_rq_byte(rqp, 8);            /* subfunction */
286         ncp_rq_byte(rqp, nmp->name_space);
287         ncp_rq_byte(rqp, 0);            /* reserved */
288         ncp_rq_word(rqp, SA_ALL);       /* search attribs: all */
289         ncp_rq_dbase_path(rqp, nmp->n_volume, dirent, namelen, name, &nmp->m.nls);
290         error = ncp_request(conn,rqp);
291         NCP_RQ_EXIT_NB;
292         return error;
293 }
294
295 int 
296 ncp_nsrename(struct ncp_conn *conn, int volume, int ns, int oldtype, 
297         struct ncp_nlstables *nt,
298         nwdirent fdir, char *old_name, int oldlen,
299         nwdirent tdir, char *new_name, int newlen,
300         struct proc *p, struct ucred *cred)
301 {
302         DECLARE_RQ;
303         int error;
304
305         NCP_RQ_HEAD(87,p,cred);
306         ncp_rq_byte(rqp, 4);
307         ncp_rq_byte(rqp, ns);
308         ncp_rq_byte(rqp, 1);
309         ncp_rq_word(rqp, oldtype);
310         /* source Handle Path */
311         ncp_rq_byte(rqp, volume);
312         ncp_rq_dword(rqp, fdir);
313         ncp_rq_byte(rqp, 1);
314         ncp_rq_byte(rqp, 1);    /* 1 source component */
315         /* dest Handle Path */
316         ncp_rq_byte(rqp, volume);
317         ncp_rq_dword(rqp, tdir);
318         ncp_rq_byte(rqp, 1);
319         ncp_rq_byte(rqp, 1);    /* 1 destination component */
320         ncp_rq_pathstring(rqp, oldlen, old_name, nt);
321         ncp_rq_pathstring(rqp, newlen, new_name, nt);
322         error = ncp_request(conn,rqp);
323         NCP_RQ_EXIT_NB;
324         return error;
325 }
326
327 int
328 ncp_modify_file_or_subdir_dos_info(struct nwmount *nmp, struct vnode *vp, 
329                                 u_int32_t info_mask,
330                                 struct nw_modify_dos_info *info,
331                                 struct proc *p,struct ucred *cred)
332 {
333         struct nwnode *np=VTONW(vp);
334         u_int8_t volnum = nmp->n_volume;
335         u_int32_t dirent = np->n_fid.f_id;
336         struct ncp_conn *conn=NWFSTOCONN(nmp);
337         int             error;
338         DECLARE_RQ;
339
340         NCP_RQ_HEAD(87,p,cred);
341         ncp_rq_byte(rqp, 7);    /* subfunction */
342         ncp_rq_byte(rqp, nmp->name_space);
343         ncp_rq_byte(rqp, 0);    /* reserved */
344         ncp_rq_word(rqp, htons(0x0680));        /* search attribs: all */
345         ncp_rq_dword(rqp, info_mask);
346         ncp_rq_mem(rqp, (caddr_t)info, sizeof(*info));
347         ncp_rq_dbase_path(rqp, volnum, dirent, 0, NULL, NULL);
348         error = ncp_request(conn,rqp);
349         NCP_RQ_EXIT_NB;
350         return error;
351 }
352
353 int
354 ncp_setattr(vp, vap, cred, procp)
355         struct vnode *vp;
356         struct vattr *vap;
357         struct ucred *cred;
358         struct proc *procp;
359 {
360         struct nwmount *nmp=VTONWFS(vp);
361         struct nwnode *np=VTONW(vp);
362         struct ncp_open_info nwn;
363         struct ncp_conn *conn=NWFSTOCONN(nmp);
364         struct nw_modify_dos_info info;
365         int error = 0, info_mask;
366         DECLARE_RQ;
367
368         if (vap->va_size != VNOVAL) {
369                 error = ncp_open_create_file_or_subdir(nmp, vp, 0, NULL, OC_MODE_OPEN, 0,
370                                                    AR_WRITE | AR_READ, &nwn,procp,cred);
371                 if (error) return error;
372                 NCP_RQ_HEAD(73,procp,cred);
373                 ncp_rq_byte(rqp, 0);
374                 ncp_rq_mem(rqp, (caddr_t)&nwn.fh, 6);
375                 ncp_rq_dword(rqp, htonl(vap->va_size));
376                 ncp_rq_word_hl(rqp, 0);
377                 checkbad(ncp_request(conn,rqp));
378                 np->n_vattr.va_size = np->n_size = vap->va_size;
379                 NCP_RQ_EXIT;
380                 ncp_close_file(conn, &nwn.fh, procp, cred);
381                 if (error) return error;
382         }
383         info_mask = 0;
384         bzero(&info, sizeof(info));
385
386         if (vap->va_mtime.tv_sec != VNOVAL) {
387                 info_mask |= (DM_MODIFY_TIME | DM_MODIFY_DATE);
388                 ncp_unix2dostime(&vap->va_mtime, nmp->m.tz, &info.modifyDate, &info.modifyTime, NULL);
389         }
390         if (vap->va_atime.tv_sec != VNOVAL) {
391                 info_mask |= (DM_LAST_ACCESS_DATE);
392                 ncp_unix2dostime(&vap->va_atime, nmp->m.tz, &info.lastAccessDate, NULL, NULL);
393         }
394         if (info_mask) {
395                 error = ncp_modify_file_or_subdir_dos_info(nmp, vp, info_mask, &info,procp,cred);
396         }
397         return (error);
398 }
399
400 int
401 ncp_get_volume_info_with_number(struct ncp_conn *conn, 
402             int n, struct ncp_volume_info *target,
403             struct proc *p,struct ucred *cred) {
404         int error,len;
405         DECLARE_RQ;
406
407         NCP_RQ_HEAD_S(22,44,p,cred);
408         ncp_rq_byte(rqp,n);
409         checkbad(ncp_request(conn,rqp));
410         target->total_blocks = ncp_rp_dword_lh(rqp);
411         target->free_blocks = ncp_rp_dword_lh(rqp);
412         target->purgeable_blocks = ncp_rp_dword_lh(rqp);
413         target->not_yet_purgeable_blocks = ncp_rp_dword_lh(rqp);
414         target->total_dir_entries = ncp_rp_dword_lh(rqp);
415         target->available_dir_entries = ncp_rp_dword_lh(rqp);
416         ncp_rp_dword_lh(rqp);
417         target->sectors_per_block = ncp_rp_byte(rqp);
418         bzero(&target->volume_name, sizeof(target->volume_name));
419         len = ncp_rp_byte(rqp);
420         if (len > NCP_VOLNAME_LEN) {
421                 error = ENAMETOOLONG;
422         } else {
423                 ncp_rp_mem(rqp,(caddr_t)&target->volume_name, len);
424         }
425         NCP_RQ_EXIT;
426         return error;
427 }
428
429 int
430 ncp_get_namespaces(struct ncp_conn *conn, u_int32_t volume, int *nsf,
431             struct proc *p,struct ucred *cred) {
432         int error;
433         u_int8_t ns;
434         u_int16_t nscnt;
435         DECLARE_RQ;
436
437         NCP_RQ_HEAD(87,p,cred);
438         ncp_rq_byte(rqp, 24);   /* Subfunction: Get Loaded Name Spaces */
439         ncp_rq_word(rqp, 0);
440         ncp_rq_byte(rqp, volume);
441         checkbad(ncp_request(conn,rqp));
442         nscnt = ncp_rp_word_lh(rqp);
443         *nsf = 0;
444         while (nscnt-- > 0) {
445                 ns = ncp_rp_byte(rqp);
446                 *nsf |= 1 << ns;
447         }
448         NCP_RQ_EXIT;
449         return error;
450 }
451
452 int
453 ncp_lookup_volume(struct ncp_conn *conn, char *volname, 
454                 u_char *volNum, u_int32_t *dirEnt,
455                 struct proc *p,struct ucred *cred)
456 {
457         int error;
458         DECLARE_RQ;
459
460         NCPNDEBUG("looking up vol %s\n", volname);
461         NCP_RQ_HEAD(87,p,cred);
462         ncp_rq_byte(rqp, 22);   /* Subfunction: Generate dir handle */
463         ncp_rq_byte(rqp, 0);    /* src name space */
464         ncp_rq_byte(rqp, 0);    /* dst name space, always zero */
465         ncp_rq_word(rqp, 0);    /* dstNSIndicator */
466
467         ncp_rq_byte(rqp, 0);    /* faked volume number */
468         ncp_rq_dword(rqp, 0);   /* faked dir_base */
469         ncp_rq_byte(rqp, 0xff); /* Don't have a dir_base */
470         ncp_rq_byte(rqp, 1);    /* 1 path component */
471         ncp_rq_pstring(rqp, volname);
472         checkbad(ncp_request(conn,rqp));
473         ncp_rp_dword_lh(rqp);   /* NSDirectoryBase*/
474         *dirEnt = ncp_rp_dword_lh(rqp);
475         *volNum = ncp_rp_byte(rqp);
476         NCP_RQ_EXIT;
477         return error;
478 }
479
480 /* 
481  * Time & date conversion routines taken from msdosfs. Although leap
482  * year calculation is bogus, it's sufficient before 2100 :)
483  */
484 /*
485  * This is the format of the contents of the deTime field in the direntry
486  * structure.
487  * We don't use bitfields because we don't know how compilers for
488  * arbitrary machines will lay them out.
489  */
490 #define DT_2SECONDS_MASK        0x1F    /* seconds divided by 2 */
491 #define DT_2SECONDS_SHIFT       0
492 #define DT_MINUTES_MASK         0x7E0   /* minutes */
493 #define DT_MINUTES_SHIFT        5
494 #define DT_HOURS_MASK           0xF800  /* hours */
495 #define DT_HOURS_SHIFT          11
496
497 /*
498  * This is the format of the contents of the deDate field in the direntry
499  * structure.
500  */
501 #define DD_DAY_MASK             0x1F    /* day of month */
502 #define DD_DAY_SHIFT            0
503 #define DD_MONTH_MASK           0x1E0   /* month */
504 #define DD_MONTH_SHIFT          5
505 #define DD_YEAR_MASK            0xFE00  /* year - 1980 */
506 #define DD_YEAR_SHIFT           9
507 /*
508  * Total number of days that have passed for each month in a regular year.
509  */
510 static u_short regyear[] = {
511         31, 59, 90, 120, 151, 181,
512         212, 243, 273, 304, 334, 365
513 };
514
515 /*
516  * Total number of days that have passed for each month in a leap year.
517  */
518 static u_short leapyear[] = {
519         31, 60, 91, 121, 152, 182,
520         213, 244, 274, 305, 335, 366
521 };
522
523 /*
524  * Variables used to remember parts of the last time conversion.  Maybe we
525  * can avoid a full conversion.
526  */
527 static u_long  lasttime;
528 static u_long  lastday;
529 static u_short lastddate;
530 static u_short lastdtime;
531 /*
532  * Convert the unix version of time to dos's idea of time to be used in
533  * file timestamps. The passed in unix time is assumed to be in GMT.
534  */
535 void
536 ncp_unix2dostime(tsp, tzoff, ddp, dtp, dhp)
537         struct timespec *tsp;
538         int tzoff;
539         u_int16_t *ddp;
540         u_int16_t *dtp;
541         u_int8_t *dhp;
542 {
543         u_long t;
544         u_long days;
545         u_long inc;
546         u_long year;
547         u_long month;
548         u_short *months;
549
550         /*
551          * If the time from the last conversion is the same as now, then
552          * skip the computations and use the saved result.
553          */
554         t = tsp->tv_sec - tzoff * 60 - tz.tz_minuteswest * 60 -
555             (wall_cmos_clock ? adjkerntz : 0);
556         t &= ~1;
557         if (lasttime != t) {
558                 lasttime = t;
559                 lastdtime = (((t / 2) % 30) << DT_2SECONDS_SHIFT)
560                     + (((t / 60) % 60) << DT_MINUTES_SHIFT)
561                     + (((t / 3600) % 24) << DT_HOURS_SHIFT);
562
563                 /*
564                  * If the number of days since 1970 is the same as the last
565                  * time we did the computation then skip all this leap year
566                  * and month stuff.
567                  */
568                 days = t / (24 * 60 * 60);
569                 if (days != lastday) {
570                         lastday = days;
571                         for (year = 1970;; year++) {
572                                 inc = year & 0x03 ? 365 : 366;
573                                 if (days < inc)
574                                         break;
575                                 days -= inc;
576                         }
577                         months = year & 0x03 ? regyear : leapyear;
578                         for (month = 0; days >= months[month]; month++)
579                                 ;
580                         if (month > 0)
581                                 days -= months[month - 1];
582                         lastddate = ((days + 1) << DD_DAY_SHIFT)
583                             + ((month + 1) << DD_MONTH_SHIFT);
584                         /*
585                          * Remember dos's idea of time is relative to 1980.
586                          * unix's is relative to 1970.  If somehow we get a
587                          * time before 1980 then don't give totally crazy
588                          * results.
589                          */
590                         if (year > 1980)
591                                 lastddate += (year - 1980) << DD_YEAR_SHIFT;
592                 }
593         }
594         if (dtp)
595                 *dtp = lastdtime;
596         if (dhp)
597                 *dhp = (tsp->tv_sec & 1) * 100 + tsp->tv_nsec / 10000000;
598
599         *ddp = lastddate;
600 }
601
602 /*
603  * The number of seconds between Jan 1, 1970 and Jan 1, 1980. In that
604  * interval there were 8 regular years and 2 leap years.
605  */
606 #define SECONDSTO1980   (((8 * 365) + (2 * 366)) * (24 * 60 * 60))
607
608 static u_short lastdosdate;
609 static u_long  lastseconds;
610
611 /*
612  * Convert from dos' idea of time to unix'. This will probably only be
613  * called from the stat(), and fstat() system calls and so probably need
614  * not be too efficient.
615  */
616 void
617 ncp_dos2unixtime(dd, dt, dh, tzoff, tsp)
618         u_int dd;
619         u_int dt;
620         u_int dh;
621         int tzoff;
622         struct timespec *tsp;
623 {
624         u_long seconds;
625         u_long month;
626         u_long year;
627         u_long days;
628         u_short *months;
629
630         if (dd == 0) {
631                 /*
632                  * Uninitialized field, return the epoch.
633                  */
634                 tsp->tv_sec = 0;
635                 tsp->tv_nsec = 0;
636                 return;
637         }
638         seconds = (((dt & DT_2SECONDS_MASK) >> DT_2SECONDS_SHIFT) << 1)
639             + ((dt & DT_MINUTES_MASK) >> DT_MINUTES_SHIFT) * 60
640             + ((dt & DT_HOURS_MASK) >> DT_HOURS_SHIFT) * 3600
641             + dh / 100;
642         /*
643          * If the year, month, and day from the last conversion are the
644          * same then use the saved value.
645          */
646         if (lastdosdate != dd) {
647                 lastdosdate = dd;
648                 days = 0;
649                 year = (dd & DD_YEAR_MASK) >> DD_YEAR_SHIFT;
650                 days = year * 365;
651                 days += year / 4 + 1;   /* add in leap days */
652                 if ((year & 0x03) == 0)
653                         days--;         /* if year is a leap year */
654                 months = year & 0x03 ? regyear : leapyear;
655                 month = (dd & DD_MONTH_MASK) >> DD_MONTH_SHIFT;
656                 if (month < 1 || month > 12) {
657                         month = 1;
658                 }
659                 if (month > 1)
660                         days += months[month - 2];
661                 days += ((dd & DD_DAY_MASK) >> DD_DAY_SHIFT) - 1;
662                 lastseconds = (days * 24 * 60 * 60) + SECONDSTO1980;
663         }
664         tsp->tv_sec = seconds + lastseconds + tz.tz_minuteswest * 60 +
665             tzoff * 60 + (wall_cmos_clock ? adjkerntz : 0);
666         tsp->tv_nsec = (dh % 100) * 10000000;
667 }