]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/fs/nwfs/nwfs_subr.c
add -n option to suppress clearing the build tree and add -DNO_CLEAN
[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/lock.h>
38 #include <sys/lockmgr.h>
39 #include <sys/malloc.h>
40 #include <sys/clock.h>
41 #include <sys/time.h>
42
43 #include <netncp/ncp.h>
44 #include <netncp/ncp_conn.h>
45 #include <netncp/ncp_ncp.h>
46 #include <netncp/ncp_subr.h>
47 #include <netncp/ncp_rq.h>
48 #include <netncp/nwerror.h>
49
50 #include <fs/nwfs/nwfs.h>
51 #include <fs/nwfs/nwfs_node.h>
52 #include <fs/nwfs/nwfs_subr.h>
53
54 #define NCP_INFOSZ      (sizeof(struct nw_entry_info) - 257)
55
56 MALLOC_DEFINE(M_NWFSDATA, "nwfs_data", "NWFS private data");
57
58 static int
59 ncp_extract_file_info(struct nwmount *nmp, struct ncp_rq *rqp,
60         struct nw_entry_info *target, int withname)
61 {
62         u_int8_t name_len;
63
64         md_get_mem(&rqp->rp, (caddr_t)target, NCP_INFOSZ, MB_MSYSTEM);
65         if (!withname)
66                 return 0;
67         md_get_uint8(&rqp->rp, &name_len);
68         target->nameLen = name_len;
69         md_get_mem(&rqp->rp, (caddr_t)target->entryName, name_len, MB_MSYSTEM);
70         target->entryName[name_len] = '\0';
71         ncp_path2unix(target->entryName, target->entryName, name_len, &nmp->m.nls);
72         return 0;
73 }
74
75 int
76 ncp_initsearch(struct vnode *dvp, struct thread *td, struct ucred *cred)
77 {
78         struct nwmount *nmp = VTONWFS(dvp);
79         struct ncp_conn *conn = NWFSTOCONN(nmp);
80         struct nwnode *np = VTONW(dvp);
81         struct ncp_rq *rqp;
82         u_int8_t volnum = nmp->n_volume;
83         u_int32_t dirent = np->n_fid.f_id;
84         int error;
85
86         NCPNDEBUG("vol=%d,dir=%d\n", volnum, dirent);
87         error = ncp_rq_alloc(87, conn, td, cred, &rqp);
88         if (error)
89                 return error;
90         mb_put_uint8(&rqp->rq, 2);              /* subfunction */
91         mb_put_uint8(&rqp->rq, nmp->name_space);
92         mb_put_uint8(&rqp->rq, 0);              /* reserved */
93         ncp_rq_dbase_path(rqp, volnum, dirent, 0, NULL, NULL);
94         rqp->nr_minrplen = sizeof(np->n_seq);
95         error = ncp_request(rqp);
96         if (error)
97                 return error;
98         md_get_mem(&rqp->rp, (caddr_t)&np->n_seq, sizeof(np->n_seq), MB_MSYSTEM);
99         ncp_rq_done(rqp);
100         return 0;
101 }
102
103 int 
104 ncp_search_for_file_or_subdir(struct nwmount *nmp,
105                               struct nw_search_seq *seq,
106                               struct nw_entry_info *target,
107                               struct thread *td,struct ucred *cred)
108 {
109         struct ncp_conn *conn = NWFSTOCONN(nmp);
110         struct ncp_rq *rqp;
111         int error;
112
113         error = ncp_rq_alloc(87, conn, td, cred, &rqp);
114         if (error)
115                 return error;
116         mb_put_uint8(&rqp->rq, 3);              /* subfunction */
117         mb_put_uint8(&rqp->rq, nmp->name_space);
118         mb_put_uint8(&rqp->rq, 0);              /* data stream */
119         mb_put_uint16le(&rqp->rq, 0xffff);      /* Search attribs */
120         mb_put_uint32le(&rqp->rq, IM_ALL);      /* return info mask */
121         mb_put_mem(&rqp->rq, (caddr_t)seq, 9, MB_MSYSTEM);
122         mb_put_uint8(&rqp->rq, 2);              /* 2 byte pattern */
123         mb_put_uint8(&rqp->rq, 0xff);           /* following is a wildcard */
124         mb_put_uint8(&rqp->rq, '*');
125         rqp->nr_minrplen = sizeof(*seq) +  1 + NCP_INFOSZ + 1;
126         error = ncp_request(rqp);
127         if (error)
128                 return error;
129         md_get_mem(&rqp->rp, (caddr_t)seq, sizeof(*seq), MB_MSYSTEM);
130         md_get_uint8(&rqp->rp, NULL);           /* skip */
131         error = ncp_extract_file_info(nmp, rqp, target, 1);
132         ncp_rq_done(rqp);
133         return error;
134 }
135
136 /*
137  * Returns information for a (one-component) name relative to the specified
138  * directory.
139  */
140 int 
141 ncp_obtain_info(struct nwmount *nmp,  u_int32_t dirent,
142                 int namelen, char *path, struct nw_entry_info *target,
143                 struct thread *td,struct ucred *cred)
144 {
145         struct ncp_conn *conn=NWFSTOCONN(nmp);
146         struct ncp_rq *rqp;
147         int error;
148         u_char volnum = nmp->n_volume, ns;
149
150         if (target == NULL) {
151                 NCPFATAL("target == NULL\n");
152                 return EINVAL;
153         }
154         ns = (path == NULL || path[0] == 0) ? NW_NS_DOS : nmp->name_space;
155         error = ncp_rq_alloc(87, conn, td, cred, &rqp);
156         if (error)
157                 return error;
158         mb_put_uint8(&rqp->rq, 6);      /* subfunction */
159         mb_put_uint8(&rqp->rq, ns);
160         mb_put_uint8(&rqp->rq, ns);     /* DestNameSpace */
161         mb_put_uint16le(&rqp->rq, 0xff);        /* get all */
162         mb_put_uint32le(&rqp->rq, IM_ALL);
163         ncp_rq_dbase_path(rqp, volnum, dirent, namelen, path, &nmp->m.nls);
164         error = ncp_request(rqp);
165         if (error)
166                 return error;
167         error = ncp_extract_file_info(nmp, rqp, target, path != NULL);
168         ncp_rq_done(rqp);
169         return error;
170 }
171 /* 
172  * lookup name pointed by cnp in directory dvp and return file info in np.
173  * May be I should create a little cache, but another way is to minimize
174  * number of calls, on other hand, in multiprocess environment ...
175  */
176 int
177 ncp_lookup(struct vnode *dvp, int len, char *name, struct nw_entry_info *fap,
178                 struct thread *td,struct ucred *cred)
179 {
180         struct nwmount *nmp;
181         struct nwnode *dnp;
182         struct ncp_conn *conn;
183         int error;
184
185         if (!dvp || dvp->v_type != VDIR) {
186                 nwfs_printf("dvp is NULL or not a directory.\n");
187                 return (ENOENT);
188         }
189         dnp = VTONW(dvp);
190         nmp = VTONWFS(dvp);
191         conn = NWFSTOCONN(nmp);
192
193         if (len == 1 && name[0] == '.') {
194                 if (dnp->n_flag & NVOLUME) {
195                         error = ncp_obtain_info(nmp, dnp->n_fid.f_id, 0, NULL,
196                                 fap, td, cred);
197                 } else {
198                         error = ncp_obtain_info(nmp, dnp->n_fid.f_parent, 
199                                 dnp->n_nmlen, dnp->n_name, fap, td, cred);
200                 }
201                 return error;
202         } else if (len == 2 && name[0] == '.' && name[1] == '.') {
203                 printf("%s: knows NOTHING about '..'\n", __func__);
204                 return EIO;
205         } else {
206                 error = ncp_obtain_info(nmp, dnp->n_fid.f_id, 
207                         len, name, fap, td, cred);
208         }
209         return error;
210 }
211
212 static void ConvertToNWfromDWORD(u_int32_t sfd, ncp_fh *fh);
213 static void 
214 ConvertToNWfromDWORD(u_int32_t sfd, ncp_fh *fh) {
215         fh->val1 = (fh->val.val32 = sfd);
216         return;
217 }
218
219 /*
220  * If both dir and name are NULL, then in target there's already a looked-up
221  * entry that wants to be opened.
222  */
223 int 
224 ncp_open_create_file_or_subdir(struct nwmount *nmp,struct vnode *dvp,int namelen,
225             char *name, int open_create_mode, u_int32_t create_attributes,
226             int desired_acc_rights, struct ncp_open_info *nop,
227             struct thread *td,struct ucred *cred)
228 {
229         
230         struct ncp_conn *conn=NWFSTOCONN(nmp);
231         struct ncp_rq *rqp;
232         u_int16_t search_attribs = SA_ALL & (~SA_SUBDIR_FILES);
233         u_int8_t volnum;
234         u_int32_t dirent;
235         int error;
236
237         error = ncp_rq_alloc(87, conn, td, cred, &rqp);
238         if (error)
239                 return error;
240         volnum = nmp->n_volume;
241         dirent = VTONW(dvp)->n_fid.f_id;
242         if ((create_attributes & aDIR) != 0) {
243                 search_attribs |= SA_SUBDIR_FILES;
244         }
245         mb_put_uint8(&rqp->rq, 1);/* subfunction */
246         mb_put_uint8(&rqp->rq, nmp->name_space);
247         mb_put_uint8(&rqp->rq, open_create_mode);
248         mb_put_uint16le(&rqp->rq, search_attribs);
249         mb_put_uint32le(&rqp->rq, IM_ALL);
250         mb_put_uint32le(&rqp->rq, create_attributes);
251         /*
252          * The desired acc rights seem to be the inherited rights mask for
253          * directories
254          */
255         mb_put_uint16le(&rqp->rq, desired_acc_rights);
256         ncp_rq_dbase_path(rqp, volnum, dirent, namelen, name, &nmp->m.nls);
257         error = ncp_request(rqp);
258         if (error) {
259                 if (error == NWE_FILE_NO_CREATE_PRIV)
260                         error = EACCES;
261                 return error;
262         }
263         md_get_uint32le(&rqp->rp, &nop->origfh);
264         md_get_uint8(&rqp->rp, &nop->action);
265         md_get_uint8(&rqp->rp, NULL);   /* skip */
266         error = ncp_extract_file_info(nmp, rqp, &nop->fattr, 1);
267         ncp_rq_done(rqp);
268         ConvertToNWfromDWORD(nop->origfh, &nop->fh);
269         return error;
270 }
271
272 int
273 ncp_close_file(struct ncp_conn *conn, ncp_fh *fh,struct thread *td,struct ucred *cred)
274 {
275         struct ncp_rq *rqp;
276         int error;
277
278         error = ncp_rq_alloc(66, conn, td, cred, &rqp);
279         if (error)
280                 return error;
281         mb_put_uint8(&rqp->rq, 0);
282         mb_put_mem(&rqp->rq, (caddr_t)fh, 6, MB_MSYSTEM);
283         error = ncp_request(rqp);
284         if (error)
285                 return error;
286         ncp_rq_done(rqp);
287         return error;
288 }
289
290 int
291 ncp_DeleteNSEntry(struct nwmount *nmp, u_int32_t dirent,
292         int namelen,char *name,struct thread *td,struct ucred *cred)
293 {
294         struct ncp_rq *rqp;
295         int error;
296         struct ncp_conn *conn=NWFSTOCONN(nmp);
297
298         error = ncp_rq_alloc(87, conn, td, cred, &rqp);
299         if (error)
300                 return error;
301         mb_put_uint8(&rqp->rq, 8);              /* subfunction */
302         mb_put_uint8(&rqp->rq, nmp->name_space);
303         mb_put_uint8(&rqp->rq, 0);              /* reserved */
304         mb_put_uint16le(&rqp->rq, SA_ALL);      /* search attribs: all */
305         ncp_rq_dbase_path(rqp, nmp->n_volume, dirent, namelen, name, &nmp->m.nls);
306         error = ncp_request(rqp);
307         if (!error)
308                 ncp_rq_done(rqp);
309         return error;
310 }
311
312 int 
313 ncp_nsrename(struct ncp_conn *conn, int volume, int ns, int oldtype, 
314         struct ncp_nlstables *nt,
315         nwdirent fdir, char *old_name, int oldlen,
316         nwdirent tdir, char *new_name, int newlen,
317         struct thread *td, struct ucred *cred)
318 {
319         struct ncp_rq *rqp;
320         int error;
321
322         error = ncp_rq_alloc(87, conn, td, cred, &rqp);
323         if (error)
324                 return error;
325         mb_put_uint8(&rqp->rq, 4);
326         mb_put_uint8(&rqp->rq, ns);
327         mb_put_uint8(&rqp->rq, 1);      /* RRenameToMySelf */
328         mb_put_uint16le(&rqp->rq, oldtype);
329         /* source Handle Path */
330         mb_put_uint8(&rqp->rq, volume);
331         mb_put_mem(&rqp->rq, (c_caddr_t)&fdir, sizeof(fdir), MB_MSYSTEM);
332         mb_put_uint8(&rqp->rq, 1);
333         mb_put_uint8(&rqp->rq, 1);      /* 1 source component */
334         /* dest Handle Path */
335         mb_put_uint8(&rqp->rq, volume);
336         mb_put_mem(&rqp->rq, (c_caddr_t)&tdir, sizeof(tdir), MB_MSYSTEM);
337         mb_put_uint8(&rqp->rq, 1);
338         mb_put_uint8(&rqp->rq, 1);      /* 1 destination component */
339         ncp_rq_pathstring(rqp, oldlen, old_name, nt);
340         ncp_rq_pathstring(rqp, newlen, new_name, nt);
341         error = ncp_request(rqp);
342         if (!error)
343                 ncp_rq_done(rqp);
344         return error;
345 }
346
347 int
348 ncp_modify_file_or_subdir_dos_info(struct nwmount *nmp, struct vnode *vp, 
349                                 u_int32_t info_mask,
350                                 struct nw_modify_dos_info *info,
351                                 struct thread *td,struct ucred *cred)
352 {
353         struct nwnode *np=VTONW(vp);
354         struct ncp_rq *rqp;
355         u_int8_t volnum = nmp->n_volume;
356         u_int32_t dirent = np->n_fid.f_id;
357         struct ncp_conn *conn=NWFSTOCONN(nmp);
358         int             error;
359
360         error = ncp_rq_alloc(87, conn, td, cred, &rqp);
361         if (error)
362                 return error;
363         mb_put_uint8(&rqp->rq, 7);      /* subfunction */
364         mb_put_uint8(&rqp->rq, nmp->name_space);
365         mb_put_uint8(&rqp->rq, 0);      /* reserved */
366         mb_put_uint16le(&rqp->rq, SA_ALL);      /* search attribs: all */
367         mb_put_uint32le(&rqp->rq, info_mask);
368         mb_put_mem(&rqp->rq, (caddr_t)info, sizeof(*info), MB_MSYSTEM);
369         ncp_rq_dbase_path(rqp, volnum, dirent, 0, NULL, NULL);
370         error = ncp_request(rqp);
371         if (!error)
372                 ncp_rq_done(rqp);
373         return error;
374 }
375
376 int
377 ncp_setattr(vp, vap, cred, td)
378         struct vnode *vp;
379         struct vattr *vap;
380         struct ucred *cred;
381         struct thread *td;
382 {
383         struct nwmount *nmp=VTONWFS(vp);
384         struct nwnode *np=VTONW(vp);
385         struct ncp_open_info nwn;
386         struct ncp_conn *conn=NWFSTOCONN(nmp);
387         struct nw_modify_dos_info info;
388         struct ncp_rq *rqp;
389         int error = 0, info_mask;
390
391         if (vap->va_size != VNOVAL) {
392                 error = ncp_open_create_file_or_subdir(nmp, vp, 0, NULL, OC_MODE_OPEN, 0,
393                                                    AR_WRITE | AR_READ, &nwn,td,cred);
394                 if (error)
395                         return error;
396                 error = ncp_rq_alloc(73, conn, td, cred, &rqp);
397                 if (error) {
398                         ncp_close_file(conn, &nwn.fh, td, cred);
399                         return error;
400                 }
401                 mb_put_uint8(&rqp->rq, 0);
402                 mb_put_mem(&rqp->rq, (caddr_t)&nwn.fh, 6, MB_MSYSTEM);
403                 mb_put_uint32be(&rqp->rq, vap->va_size);
404                 mb_put_uint16be(&rqp->rq, 0);
405                 error = ncp_request(rqp);
406                 np->n_vattr.va_size = np->n_size = vap->va_size;
407                 if (!error)
408                         ncp_rq_done(rqp);
409                 ncp_close_file(conn, &nwn.fh, td, cred);
410                 if (error)
411                         return error;
412         }
413         info_mask = 0;
414         bzero(&info, sizeof(info));
415
416         if (vap->va_mtime.tv_sec != VNOVAL) {
417                 info_mask |= (DM_MODIFY_TIME | DM_MODIFY_DATE);
418                 ncp_unix2dostime(&vap->va_mtime, nmp->m.tz, &info.modifyDate, &info.modifyTime, NULL);
419         }
420         if (vap->va_atime.tv_sec != VNOVAL) {
421                 info_mask |= (DM_LAST_ACCESS_DATE);
422                 ncp_unix2dostime(&vap->va_atime, nmp->m.tz, &info.lastAccessDate, NULL, NULL);
423         }
424         if (info_mask) {
425                 error = ncp_modify_file_or_subdir_dos_info(nmp, vp, info_mask, &info,td,cred);
426         }
427         return (error);
428 }
429
430 int
431 ncp_get_volume_info_with_number(struct ncp_conn *conn, 
432         int n, struct ncp_volume_info *target,
433         struct thread *td,struct ucred *cred)
434 {
435         struct ncp_rq *rqp;
436         u_int32_t tmp32;
437         u_int8_t len;
438         int error;
439
440         error = ncp_rq_alloc_subfn(22, 44, conn, td, cred, &rqp);
441         if (error)
442                 return error;
443         mb_put_uint8(&rqp->rq,n);
444         error = ncp_request(rqp);
445         if (error)
446                 return error;
447         md_get_uint32le(&rqp->rp, &target->total_blocks);
448         md_get_uint32le(&rqp->rp, &target->free_blocks);
449         md_get_uint32le(&rqp->rp, &target->purgeable_blocks);
450         md_get_uint32le(&rqp->rp, &target->not_yet_purgeable_blocks);
451         md_get_uint32le(&rqp->rp, &target->total_dir_entries);
452         md_get_uint32le(&rqp->rp, &target->available_dir_entries);
453         md_get_uint32le(&rqp->rp, &tmp32);
454         md_get_uint8(&rqp->rp, &target->sectors_per_block);
455         bzero(&target->volume_name, sizeof(target->volume_name));
456         md_get_uint8(&rqp->rp, &len);
457         if (len > NCP_VOLNAME_LEN) {
458                 error = ENAMETOOLONG;
459         } else {
460                 md_get_mem(&rqp->rp, (caddr_t)&target->volume_name, len, MB_MSYSTEM);
461         }
462         ncp_rq_done(rqp);
463         return error;
464 }
465
466 int
467 ncp_get_namespaces(struct ncp_conn *conn, u_int32_t volume, int *nsf,
468         struct thread *td,struct ucred *cred)
469 {
470         struct ncp_rq *rqp;
471         int error;
472         u_int8_t ns;
473         u_int16_t nscnt;
474
475         error = ncp_rq_alloc(87, conn, td, cred, &rqp);
476         if (error)
477                 return error;
478         mb_put_uint8(&rqp->rq, 24);     /* Subfunction: Get Loaded Name Spaces */
479         mb_put_uint16le(&rqp->rq, 0);   /* reserved */
480         mb_put_uint8(&rqp->rq, volume);
481         error = ncp_request(rqp);
482         if (error)
483                 return error;
484         md_get_uint16le(&rqp->rp, &nscnt);
485         *nsf = 0;
486         while (nscnt-- > 0) {
487                 md_get_uint8(&rqp->rp, &ns);
488                 *nsf |= 1 << ns;
489         }
490         ncp_rq_done(rqp);
491         return error;
492 }
493
494 int
495 ncp_lookup_volume(struct ncp_conn *conn, char *volname, 
496                 u_char *volNum, u_int32_t *dirEnt,
497                 struct thread *td,struct ucred *cred)
498 {
499         struct ncp_rq *rqp;
500         u_int32_t tmp32;
501         int error;
502
503         NCPNDEBUG("looking up vol %s\n", volname);
504         error = ncp_rq_alloc(87, conn, td, cred, &rqp);
505         if (error)
506                 return error;
507         mb_put_uint8(&rqp->rq, 22);     /* Subfunction: Generate dir handle */
508         mb_put_uint8(&rqp->rq, 0);      /* src name space */
509         mb_put_uint8(&rqp->rq, 0);      /* dst name space, always zero */
510         mb_put_uint16le(&rqp->rq, 0);   /* dstNSIndicator (Jn) */
511
512         mb_put_uint8(&rqp->rq, 0);      /* faked volume number */
513         mb_put_uint32be(&rqp->rq, 0);   /* faked dir_base */
514         mb_put_uint8(&rqp->rq, 0xff);   /* Don't have a dir_base */
515         mb_put_uint8(&rqp->rq, 1);      /* 1 path component */
516         ncp_rq_pstring(rqp, volname);
517         error = ncp_request(rqp);
518         if (error)
519                 return error;
520         md_get_uint32le(&rqp->rp, &tmp32);
521         md_get_uint32le(&rqp->rp, dirEnt);
522         md_get_uint8(&rqp->rp, volNum);
523         ncp_rq_done(rqp);
524         return error;
525 }
526
527 /*
528  * XXX: I think the timezone in struct nwfs_args is truly bogus, especially
529  * XXX: considering that nwfs_mount(8) picks this up from the kernel in
530  * XXX: the first place.  Since I can't test this, I won't attempt to fix it.
531  * XXX: /phk
532  */
533
534 void
535 ncp_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         struct timespec t;
543
544         t = *tsp;
545         
546         t.tv_sec = - tzoff * 60 - utc_offset();
547         timespec2fattime(&t, 1, ddp, dtp, dhp);
548 }
549
550 void
551 ncp_dos2unixtime(dd, dt, dh, tzoff, tsp)
552         u_int dd;
553         u_int dt;
554         u_int dh;
555         int tzoff;
556         struct timespec *tsp;
557 {
558
559         fattime2timespec(dd, dt, dh, 1, tsp);
560         tsp->tv_sec += tzoff * 60 + utc_offset();
561 }