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