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