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