dma: factor out mail handling code
[dragonfly.git] / sys / vfs / nwfs / nwfs_subr.c
blobb69e3e0f1a1798f39bef4579bd30a0c9a29ce0d2
1 /*
2 * Copyright (c) 1999, Boris Popov
3 * All rights reserved.
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.
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.
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.8 2006/12/23 00:41:30 swildner Exp $
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>
42 #include <netproto/ncp/ncp.h>
43 #include <netproto/ncp/ncp_conn.h>
44 #include <netproto/ncp/ncp_ncp.h>
45 #include <netproto/ncp/ncp_subr.h>
46 #include <netproto/ncp/ncp_rq.h>
47 #include <netproto/ncp/nwerror.h>
49 #include "nwfs.h"
50 #include "nwfs_node.h"
51 #include "nwfs_subr.h"
53 MALLOC_DEFINE(M_NWFSDATA, "NWFS data", "NWFS private data");
55 static void
56 ncp_extract_file_info(struct nwmount *nmp, struct ncp_rq *rqp,
57 struct nw_entry_info *target)
59 u_char name_len;
60 const int info_struct_size = sizeof(struct nw_entry_info) - 257;
62 ncp_rp_mem(rqp,(caddr_t)target,info_struct_size);
63 name_len = ncp_rp_byte(rqp);
64 target->nameLen = name_len;
65 ncp_rp_mem(rqp,(caddr_t)target->entryName, name_len);
66 target->entryName[name_len] = '\0';
67 ncp_path2unix(target->entryName, target->entryName, name_len, &nmp->m.nls);
68 return;
71 static void
72 ncp_update_file_info(struct nwmount *nmp, struct ncp_rq *rqp,
73 struct nw_entry_info *target)
75 int info_struct_size = sizeof(struct nw_entry_info) - 257;
77 ncp_rp_mem(rqp,(caddr_t)target,info_struct_size);
78 return;
81 int
82 ncp_initsearch(struct vnode *dvp, struct thread *td, struct ucred *cred)
84 struct nwmount *nmp = VTONWFS(dvp);
85 struct ncp_conn *conn = NWFSTOCONN(nmp);
86 struct nwnode *np = VTONW(dvp);
87 u_int8_t volnum = nmp->n_volume;
88 u_int32_t dirent = np->n_fid.f_id;
89 int error;
90 DECLARE_RQ;
92 NCPNDEBUG("vol=%d,dir=%d\n", volnum, dirent);
93 NCP_RQ_HEAD(87,td,cred);
94 ncp_rq_byte(rqp, 2); /* subfunction */
95 ncp_rq_byte(rqp, nmp->name_space);
96 ncp_rq_byte(rqp, 0); /* reserved */
97 ncp_rq_dbase_path(rqp, volnum, dirent, 0, NULL, NULL);
98 checkbad(ncp_request(conn,rqp));
99 ncp_rp_mem(rqp,(caddr_t)&np->n_seq, sizeof(np->n_seq));
100 NCP_RQ_EXIT;
101 return error;
104 int
105 ncp_search_for_file_or_subdir(struct nwmount *nmp,
106 struct nw_search_seq *seq,
107 struct nw_entry_info *target,
108 struct thread *td, struct ucred *cred)
110 struct ncp_conn *conn = NWFSTOCONN(nmp);
111 int error;
112 DECLARE_RQ;
114 NCP_RQ_HEAD(87,td,cred);
115 ncp_rq_byte(rqp, 3); /* subfunction */
116 ncp_rq_byte(rqp, nmp->name_space);
117 ncp_rq_byte(rqp, 0); /* data stream */
118 ncp_rq_word_lh(rqp, 0xffff); /* Search attribs */
119 ncp_rq_dword(rqp, IM_ALL); /* return info mask */
120 ncp_rq_mem(rqp, (caddr_t)seq, 9);
121 ncp_rq_byte(rqp, 2); /* 2 byte pattern */
122 ncp_rq_byte(rqp, 0xff); /* following is a wildcard */
123 ncp_rq_byte(rqp, '*');
124 checkbad(ncp_request(conn,rqp));
125 ncp_rp_mem(rqp,(caddr_t)seq, sizeof(*seq));
126 ncp_rp_byte(rqp); /* skip */
127 ncp_extract_file_info(nmp, rqp, target);
128 NCP_RQ_EXIT;
129 return error;
133 * Returns information for a (one-component) name relative to the specified
134 * directory.
136 int
137 ncp_obtain_info(struct nwmount *nmp, u_int32_t dirent,
138 int namelen, char *path, struct nw_entry_info *target,
139 struct thread *td, struct ucred *cred)
141 struct ncp_conn *conn=NWFSTOCONN(nmp);
142 int error;
143 u_char volnum = nmp->n_volume, ns;
144 DECLARE_RQ;
146 if (target == NULL) {
147 NCPFATAL("target == NULL\n");
148 return EINVAL;
150 ns = (path == NULL || path[0] == 0) ? NW_NS_DOS : nmp->name_space;
151 NCP_RQ_HEAD(87, td, cred);
152 ncp_rq_byte(rqp, 6); /* subfunction */
153 ncp_rq_byte(rqp, ns);
154 ncp_rq_byte(rqp, ns); /* DestNameSpace */
155 ncp_rq_word(rqp, htons(0xff00)); /* get all */
156 ncp_rq_dword(rqp, IM_ALL);
157 ncp_rq_dbase_path(rqp, volnum, dirent, namelen, path, &nmp->m.nls);
158 checkbad(ncp_request(conn,rqp));
159 if (path)
160 ncp_extract_file_info(nmp, rqp, target);
161 else
162 ncp_update_file_info(nmp, rqp, target);
163 NCP_RQ_EXIT;
164 return error;
167 * lookup name pointed by cnp in directory dvp and return file info in np.
168 * May be I should create a little cache, but another way is to minimize
169 * number of calls, on other hand, in multiprocess environment ...
172 ncp_lookup(struct vnode *dvp, int len, char *name, struct nw_entry_info *fap,
173 struct thread *td, struct ucred *cred)
175 struct nwmount *nmp;
176 struct nwnode *dnp = VTONW(dvp);
177 struct ncp_conn *conn;
178 int error;
180 if (!dvp || dvp->v_type != VDIR) {
181 nwfs_printf("dvp is NULL or not a directory.\n");
182 return (ENOENT);
184 nmp = VTONWFS(dvp);
185 conn = NWFSTOCONN(nmp);
187 if (len == 1 && name[0] == '.') {
188 if (strcmp(dnp->n_name, NWFS_ROOTVOL) == 0) {
189 error = ncp_obtain_info(nmp, dnp->n_fid.f_id, 0, NULL,
190 fap, td, cred);
191 } else {
192 error = ncp_obtain_info(nmp, dnp->n_fid.f_parent,
193 dnp->n_nmlen, dnp->n_name, fap, td, cred);
195 return error;
196 } else if (len == 2 && name[0] == '.' && name[1] == '.') {
197 kprintf("%s: knows NOTHING about '..'\n", __func__);
198 return EIO;
199 } else {
200 error = ncp_obtain_info(nmp, dnp->n_fid.f_id,
201 len, name, fap, td, cred);
203 return error;
206 static void ConvertToNWfromDWORD(u_int32_t sfd, ncp_fh *fh);
207 static void
208 ConvertToNWfromDWORD(u_int32_t sfd, ncp_fh *fh) {
209 fh->val1 = (fh->val.val32 = sfd);
210 return;
214 * If both dir and name are NULL, then in target there's already a looked-up
215 * entry that wants to be opened.
217 int
218 ncp_open_create_file_or_subdir(struct nwmount *nmp, struct vnode *dvp,
219 int namelen, char *name, int open_create_mode,
220 u_int32_t create_attributes,
221 int desired_acc_rights,
222 struct ncp_open_info *nop,
223 struct thread *td, struct ucred *cred)
226 struct ncp_conn *conn=NWFSTOCONN(nmp);
227 u_int16_t search_attribs = SA_ALL & (~SA_SUBDIR_FILES);
228 u_int8_t volnum;
229 u_int32_t dirent;
230 int error;
231 DECLARE_RQ;
233 volnum = nmp->n_volume;
234 dirent = VTONW(dvp)->n_fid.f_id;
235 if ((create_attributes & aDIR) != 0) {
236 search_attribs |= SA_SUBDIR_FILES;
238 NCP_RQ_HEAD(87,td,cred);
239 ncp_rq_byte(rqp, 1);/* subfunction */
240 ncp_rq_byte(rqp, nmp->name_space);
241 ncp_rq_byte(rqp, open_create_mode);
242 ncp_rq_word(rqp, search_attribs);
243 ncp_rq_dword(rqp, IM_ALL);
244 ncp_rq_dword(rqp, create_attributes);
246 * The desired acc rights seem to be the inherited rights mask for
247 * directories
249 ncp_rq_word(rqp, desired_acc_rights);
250 ncp_rq_dbase_path(rqp, volnum, dirent, namelen, name, &nmp->m.nls);
251 checkbad(ncp_request(conn,rqp));
253 nop->origfh = ncp_rp_dword_lh(rqp);
254 nop->action = ncp_rp_byte(rqp);
255 ncp_rp_byte(rqp); /* skip */
256 ncp_extract_file_info(nmp, rqp, &nop->fattr);
257 ConvertToNWfromDWORD(nop->origfh, &nop->fh);
258 NCP_RQ_EXIT;
259 switch(error) {
260 case NWE_FILE_NO_CREATE_PRIV:
261 error = EACCES;
262 break;
264 return error;
268 ncp_close_file(struct ncp_conn *conn, ncp_fh *fh, struct thread *td,
269 struct ucred *cred)
271 int error;
272 DECLARE_RQ;
274 NCP_RQ_HEAD(66,td,cred);
275 ncp_rq_byte(rqp, 0);
276 ncp_rq_mem(rqp, (caddr_t)fh, 6);
277 error = ncp_request(conn,rqp);
278 NCP_RQ_EXIT_NB;
279 return error;
283 ncp_DeleteNSEntry(struct nwmount *nmp, u_int32_t dirent, int namelen,
284 char *name, struct thread *td, struct ucred *cred)
286 int error;
287 struct ncp_conn *conn=NWFSTOCONN(nmp);
288 DECLARE_RQ;
290 NCP_RQ_HEAD(87,td,cred);
291 ncp_rq_byte(rqp, 8); /* subfunction */
292 ncp_rq_byte(rqp, nmp->name_space);
293 ncp_rq_byte(rqp, 0); /* reserved */
294 ncp_rq_word(rqp, SA_ALL); /* search attribs: all */
295 ncp_rq_dbase_path(rqp, nmp->n_volume, dirent, namelen, name, &nmp->m.nls);
296 error = ncp_request(conn,rqp);
297 NCP_RQ_EXIT_NB;
298 return error;
301 int
302 ncp_nsrename(struct ncp_conn *conn, int volume, int ns, int oldtype,
303 struct ncp_nlstables *nt, nwdirent fdir, char *old_name,
304 int oldlen, nwdirent tdir, char *new_name, int newlen,
305 struct thread *td, struct ucred *cred)
307 DECLARE_RQ;
308 int error;
310 NCP_RQ_HEAD(87,td,cred);
311 ncp_rq_byte(rqp, 4);
312 ncp_rq_byte(rqp, ns);
313 ncp_rq_byte(rqp, 1);
314 ncp_rq_word(rqp, oldtype);
315 /* source Handle Path */
316 ncp_rq_byte(rqp, volume);
317 ncp_rq_dword(rqp, fdir);
318 ncp_rq_byte(rqp, 1);
319 ncp_rq_byte(rqp, 1); /* 1 source component */
320 /* dest Handle Path */
321 ncp_rq_byte(rqp, volume);
322 ncp_rq_dword(rqp, tdir);
323 ncp_rq_byte(rqp, 1);
324 ncp_rq_byte(rqp, 1); /* 1 destination component */
325 ncp_rq_pathstring(rqp, oldlen, old_name, nt);
326 ncp_rq_pathstring(rqp, newlen, new_name, nt);
327 error = ncp_request(conn,rqp);
328 NCP_RQ_EXIT_NB;
329 return error;
333 ncp_modify_file_or_subdir_dos_info(struct nwmount *nmp, struct vnode *vp,
334 u_int32_t info_mask,
335 struct nw_modify_dos_info *info,
336 struct thread *td, struct ucred *cred)
338 struct nwnode *np=VTONW(vp);
339 u_int8_t volnum = nmp->n_volume;
340 u_int32_t dirent = np->n_fid.f_id;
341 struct ncp_conn *conn=NWFSTOCONN(nmp);
342 int error;
343 DECLARE_RQ;
345 NCP_RQ_HEAD(87,td,cred);
346 ncp_rq_byte(rqp, 7); /* subfunction */
347 ncp_rq_byte(rqp, nmp->name_space);
348 ncp_rq_byte(rqp, 0); /* reserved */
349 ncp_rq_word(rqp, htons(0x0680)); /* search attribs: all */
350 ncp_rq_dword(rqp, info_mask);
351 ncp_rq_mem(rqp, (caddr_t)info, sizeof(*info));
352 ncp_rq_dbase_path(rqp, volnum, dirent, 0, NULL, NULL);
353 error = ncp_request(conn,rqp);
354 NCP_RQ_EXIT_NB;
355 return error;
359 ncp_setattr(struct vnode *vp, struct vattr *vap, struct ucred *cred,
360 struct thread *td)
362 struct nwmount *nmp=VTONWFS(vp);
363 struct nwnode *np=VTONW(vp);
364 struct ncp_open_info nwn;
365 struct ncp_conn *conn=NWFSTOCONN(nmp);
366 struct nw_modify_dos_info info;
367 int error = 0, info_mask;
368 DECLARE_RQ;
370 if (vap->va_size != VNOVAL) {
371 error = ncp_open_create_file_or_subdir(
372 nmp, vp, 0, NULL, OC_MODE_OPEN, 0,
373 AR_WRITE | AR_READ, &nwn,td,cred);
374 if (error) return error;
375 NCP_RQ_HEAD(73,td,cred);
376 ncp_rq_byte(rqp, 0);
377 ncp_rq_mem(rqp, (caddr_t)&nwn.fh, 6);
378 ncp_rq_dword(rqp, htonl(vap->va_size));
379 ncp_rq_word_hl(rqp, 0);
380 checkbad(ncp_request(conn,rqp));
381 np->n_vattr.va_size = np->n_size = vap->va_size;
382 NCP_RQ_EXIT;
383 ncp_close_file(conn, &nwn.fh, td, cred);
384 if (error) return error;
386 info_mask = 0;
387 bzero(&info, sizeof(info));
389 if (vap->va_mtime.tv_sec != VNOVAL) {
390 info_mask |= (DM_MODIFY_TIME | DM_MODIFY_DATE);
391 ncp_unix2dostime(&vap->va_mtime, nmp->m.tz, &info.modifyDate, &info.modifyTime, NULL);
393 if (vap->va_atime.tv_sec != VNOVAL) {
394 info_mask |= (DM_LAST_ACCESS_DATE);
395 ncp_unix2dostime(&vap->va_atime, nmp->m.tz, &info.lastAccessDate, NULL, NULL);
397 if (info_mask) {
398 error = ncp_modify_file_or_subdir_dos_info(nmp, vp, info_mask, &info,td,cred);
400 return (error);
404 ncp_get_volume_info_with_number(struct ncp_conn *conn, int n,
405 struct ncp_volume_info *target,
406 struct thread *td, struct ucred *cred)
408 int error,len;
409 DECLARE_RQ;
411 NCP_RQ_HEAD_S(22,44,td,cred);
412 ncp_rq_byte(rqp,n);
413 checkbad(ncp_request(conn,rqp));
414 target->total_blocks = ncp_rp_dword_lh(rqp);
415 target->free_blocks = ncp_rp_dword_lh(rqp);
416 target->purgeable_blocks = ncp_rp_dword_lh(rqp);
417 target->not_yet_purgeable_blocks = ncp_rp_dword_lh(rqp);
418 target->total_dir_entries = ncp_rp_dword_lh(rqp);
419 target->available_dir_entries = ncp_rp_dword_lh(rqp);
420 ncp_rp_dword_lh(rqp);
421 target->sectors_per_block = ncp_rp_byte(rqp);
422 bzero(&target->volume_name, sizeof(target->volume_name));
423 len = ncp_rp_byte(rqp);
424 if (len > NCP_VOLNAME_LEN) {
425 error = ENAMETOOLONG;
426 } else {
427 ncp_rp_mem(rqp,(caddr_t)&target->volume_name, len);
429 NCP_RQ_EXIT;
430 return error;
434 ncp_get_namespaces(struct ncp_conn *conn, u_int32_t volume, int *nsf,
435 struct thread *td, struct ucred *cred)
437 int error;
438 u_int8_t ns;
439 u_int16_t nscnt;
440 DECLARE_RQ;
442 NCP_RQ_HEAD(87,td,cred);
443 ncp_rq_byte(rqp, 24); /* Subfunction: Get Loaded Name Spaces */
444 ncp_rq_word(rqp, 0);
445 ncp_rq_byte(rqp, volume);
446 checkbad(ncp_request(conn,rqp));
447 nscnt = ncp_rp_word_lh(rqp);
448 *nsf = 0;
449 while (nscnt-- > 0) {
450 ns = ncp_rp_byte(rqp);
451 *nsf |= 1 << ns;
453 NCP_RQ_EXIT;
454 return error;
458 ncp_lookup_volume(struct ncp_conn *conn, char *volname,
459 u_char *volNum, u_int32_t *dirEnt,
460 struct thread *td, struct ucred *cred)
462 int error;
463 DECLARE_RQ;
465 NCPNDEBUG("looking up vol %s\n", volname);
466 NCP_RQ_HEAD(87,td,cred);
467 ncp_rq_byte(rqp, 22); /* Subfunction: Generate dir handle */
468 ncp_rq_byte(rqp, 0); /* src name space */
469 ncp_rq_byte(rqp, 0); /* dst name space, always zero */
470 ncp_rq_word(rqp, 0); /* dstNSIndicator */
472 ncp_rq_byte(rqp, 0); /* faked volume number */
473 ncp_rq_dword(rqp, 0); /* faked dir_base */
474 ncp_rq_byte(rqp, 0xff); /* Don't have a dir_base */
475 ncp_rq_byte(rqp, 1); /* 1 path component */
476 ncp_rq_pstring(rqp, volname);
477 checkbad(ncp_request(conn,rqp));
478 ncp_rp_dword_lh(rqp); /* NSDirectoryBase*/
479 *dirEnt = ncp_rp_dword_lh(rqp);
480 *volNum = ncp_rp_byte(rqp);
481 NCP_RQ_EXIT;
482 return error;
486 * Time & date conversion routines taken from msdosfs. Although leap
487 * year calculation is bogus, it's sufficient before 2100 :)
490 * This is the format of the contents of the deTime field in the direntry
491 * structure.
492 * We don't use bitfields because we don't know how compilers for
493 * arbitrary machines will lay them out.
495 #define DT_2SECONDS_MASK 0x1F /* seconds divided by 2 */
496 #define DT_2SECONDS_SHIFT 0
497 #define DT_MINUTES_MASK 0x7E0 /* minutes */
498 #define DT_MINUTES_SHIFT 5
499 #define DT_HOURS_MASK 0xF800 /* hours */
500 #define DT_HOURS_SHIFT 11
503 * This is the format of the contents of the deDate field in the direntry
504 * structure.
506 #define DD_DAY_MASK 0x1F /* day of month */
507 #define DD_DAY_SHIFT 0
508 #define DD_MONTH_MASK 0x1E0 /* month */
509 #define DD_MONTH_SHIFT 5
510 #define DD_YEAR_MASK 0xFE00 /* year - 1980 */
511 #define DD_YEAR_SHIFT 9
513 * Total number of days that have passed for each month in a regular year.
515 static u_short regyear[] = {
516 31, 59, 90, 120, 151, 181,
517 212, 243, 273, 304, 334, 365
521 * Total number of days that have passed for each month in a leap year.
523 static u_short leapyear[] = {
524 31, 60, 91, 121, 152, 182,
525 213, 244, 274, 305, 335, 366
529 * Variables used to remember parts of the last time conversion. Maybe we
530 * can avoid a full conversion.
532 static u_long lasttime;
533 static u_long lastday;
534 static u_short lastddate;
535 static u_short lastdtime;
537 * Convert the unix version of time to dos's idea of time to be used in
538 * file timestamps. The passed in unix time is assumed to be in GMT.
540 void
541 ncp_unix2dostime(struct timespec *tsp, int tzoff, u_int16_t *ddp,
542 u_int16_t *dtp, u_int8_t *dhp)
544 u_long t;
545 u_long days;
546 u_long inc;
547 u_long year;
548 u_long month;
549 u_short *months;
552 * If the time from the last conversion is the same as now, then
553 * skip the computations and use the saved result.
555 t = tsp->tv_sec - tzoff * 60 - tz.tz_minuteswest * 60 -
556 (wall_cmos_clock ? adjkerntz : 0);
557 t &= ~1;
558 if (lasttime != t) {
559 lasttime = t;
560 lastdtime = (((t / 2) % 30) << DT_2SECONDS_SHIFT)
561 + (((t / 60) % 60) << DT_MINUTES_SHIFT)
562 + (((t / 3600) % 24) << DT_HOURS_SHIFT);
565 * If the number of days since 1970 is the same as the last
566 * time we did the computation then skip all this leap year
567 * and month stuff.
569 days = t / (24 * 60 * 60);
570 if (days != lastday) {
571 lastday = days;
572 for (year = 1970;; year++) {
573 inc = year & 0x03 ? 365 : 366;
574 if (days < inc)
575 break;
576 days -= inc;
578 months = year & 0x03 ? regyear : leapyear;
579 for (month = 0; days >= months[month]; month++)
581 if (month > 0)
582 days -= months[month - 1];
583 lastddate = ((days + 1) << DD_DAY_SHIFT)
584 + ((month + 1) << DD_MONTH_SHIFT);
586 * Remember dos's idea of time is relative to 1980.
587 * unix's is relative to 1970. If somehow we get a
588 * time before 1980 then don't give totally crazy
589 * results.
591 if (year > 1980)
592 lastddate += (year - 1980) << DD_YEAR_SHIFT;
595 if (dtp)
596 *dtp = lastdtime;
597 if (dhp)
598 *dhp = (tsp->tv_sec & 1) * 100 + tsp->tv_nsec / 10000000;
600 *ddp = lastddate;
604 * The number of seconds between Jan 1, 1970 and Jan 1, 1980. In that
605 * interval there were 8 regular years and 2 leap years.
607 #define SECONDSTO1980 (((8 * 365) + (2 * 366)) * (24 * 60 * 60))
609 static u_short lastdosdate;
610 static u_long lastseconds;
613 * Convert from dos' idea of time to unix'. This will probably only be
614 * called from the stat(), and fstat() system calls and so probably need
615 * not be too efficient.
617 void
618 ncp_dos2unixtime(u_int dd, u_int dt, u_int dh, int tzoff, struct timespec *tsp)
620 u_long seconds;
621 u_long month;
622 u_long year;
623 u_long days;
624 u_short *months;
626 if (dd == 0) {
628 * Uninitialized field, return the epoch.
630 tsp->tv_sec = 0;
631 tsp->tv_nsec = 0;
632 return;
634 seconds = (((dt & DT_2SECONDS_MASK) >> DT_2SECONDS_SHIFT) << 1)
635 + ((dt & DT_MINUTES_MASK) >> DT_MINUTES_SHIFT) * 60
636 + ((dt & DT_HOURS_MASK) >> DT_HOURS_SHIFT) * 3600
637 + dh / 100;
639 * If the year, month, and day from the last conversion are the
640 * same then use the saved value.
642 if (lastdosdate != dd) {
643 lastdosdate = dd;
644 days = 0;
645 year = (dd & DD_YEAR_MASK) >> DD_YEAR_SHIFT;
646 days = year * 365;
647 days += year / 4 + 1; /* add in leap days */
648 if ((year & 0x03) == 0)
649 days--; /* if year is a leap year */
650 months = year & 0x03 ? regyear : leapyear;
651 month = (dd & DD_MONTH_MASK) >> DD_MONTH_SHIFT;
652 if (month < 1 || month > 12) {
653 month = 1;
655 if (month > 1)
656 days += months[month - 2];
657 days += ((dd & DD_DAY_MASK) >> DD_DAY_SHIFT) - 1;
658 lastseconds = (days * 24 * 60 * 60) + SECONDSTO1980;
660 tsp->tv_sec = seconds + lastseconds + tz.tz_minuteswest * 60 +
661 tzoff * 60 + (wall_cmos_clock ? adjkerntz : 0);
662 tsp->tv_nsec = (dh % 100) * 10000000;