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