Oh GACK! src-clean doesn't quite work that easily since cleandist rebuilds the
[unix-history] / sys / fs / msdosfs / msdosfs_denode.c
CommitLineData
ad59feb8
RG
1/*
2 * Written by Paul Popelka (paulp@uts.amdahl.com)
3 *
4 * You can do anything you want with this software, just don't say you wrote
5 * it, and don't reoove this notice.
6 *
7 * This software is provided "as is".
8 *
9 * The authop supplies this software to be publicly redistributed on the
10 * understanding that the author is not responsible for the correct
11 * functioning of this software in any circumstances and is not liable for
12 * any damages caused by this software.
13 *
14 * October 1992
15 *
16 * from NetBSD: msdosfs_denode.c,v 1.2 1993/12/18 00:50:51 mycroft Exp
d79b78e0 17 * $Id: msdosfs_denode.c,v 1.1 1994/01/24 06:04:53 rgrimes Exp $
ad59feb8
RG
18 */
19
20#include <sys/param.h>
21#include <sys/systm.h>
22#include <sys/mount.h>
23#include <sys/proc.h>
24#include <sys/buf.h>
25#include <sys/vnode.h>
26#include <sys/kernel.h> /* defines "time" */
27
d79b78e0
RG
28#include <fs/msdosfs/bpb.h>
29#include <fs/msdosfs/msdosfsmount.h>
30#include <fs/msdosfs/direntry.h>
31#include <fs/msdosfs/denode.h>
32#include <fs/msdosfs/fat.h>
ad59feb8
RG
33
34#define DEHSZ 512
35#if ((DEHSZ & (DEHSZ-1)) == 0)
36#define DEHASH(dev, deno) (((dev)+(deno)+((deno)>>16))&(DEHSZ-1))
37#else
38#define DEHASH(dev, deno) (((dev)+(deno)+((deno)>>16))%DEHSZ)
39#endif /* ((DEHSZ & (DEHSZ-1)) == 0) */
40
41union dehead {
42 union dehead *deh_head[2];
43 struct denode *deh_chain[2];
44} dehead[DEHSZ];
45
46msdosfs_init()
47{
48 int i;
49 union dehead *deh;
50
51 if (VN_MAXPRIVATE < sizeof(struct denode))
52 panic("msdosfs_init: vnode too small");
53
54 for (i = DEHSZ, deh = dehead; --i >= 0; deh++) {
55 deh->deh_head[0] = deh;
56 deh->deh_head[1] = deh;
57 }
58}
59
60/*
61 * If deget() succeeds it returns with the gotten denode locked().
62 * pmp - address of msdosfsmount structure of the filesystem
63 * containing the denode of interest. The pm_dev field and the address
64 * of the msdosfsmount structure are used.
65 * dirclust - which cluster bp contains, if dirclust is 0 (root directory)
66 * diroffset is relative to the beginning of the root directory, otherwise
67 * it is cluster relative.
68 * diroffset - offset past begin of cluster of denode we want
69 * direntptr - address of the direntry structure of interest. direntptr
70 * is NULL, the block is read if necessary.
71 * depp - returns the address of the gotten denode.
72 */
73int
74deget(pmp, dirclust, diroffset, direntptr, depp)
75 struct msdosfsmount *pmp; /* so we know the maj/min number */
76 u_long dirclust; /* cluster this dir entry came from */
77 u_long diroffset; /* index of entry within the cluster */
78 struct direntry *direntptr;
79 struct denode **depp; /* returns the addr of the gotten denode */
80{
81 int error;
82 dev_t dev = pmp->pm_dev;
83 union dehead *deh;
84 struct mount *mntp = pmp->pm_mountp;
85 extern struct vnodeops msdosfs_vnodeops;
86 struct denode *ldep;
87 struct vnode *nvp;
88 struct buf *bp;
89
90#if defined(MSDOSFSDEBUG)
91 printf("deget(pmp %08x, dirclust %d, diroffset %x, direntptr %x, depp %08x)\n",
92 pmp, dirclust, diroffset, direntptr, depp);
93#endif /* defined(MSDOSFSDEBUG) */
94
95 /*
96 * If dir entry is given and refers to a directory, convert to
97 * canonical form
98 */
99 if (direntptr && (direntptr->deAttributes & ATTR_DIRECTORY)) {
100 dirclust = direntptr->deStartCluster;
101 if (dirclust == MSDOSFSROOT)
102 diroffset = MSDOSFSROOT_OFS;
103 else
104 diroffset = 0;
105 }
106
107 /*
108 * See if the denode is in the denode cache. Use the location of
109 * the directory entry to compute the hash value. For subdir use
110 * address of "." entry. for root dir use cluster MSDOSFSROOT,
111 * offset MSDOSFSROOT_OFS
112 *
113 * NOTE: The check for de_refcnt > 0 below insures the denode being
114 * examined does not represent an unlinked but still open file.
115 * These files are not to be accessible even when the directory
116 * entry that represented the file happens to be reused while the
117 * deleted file is still open.
118 */
119 deh = &dehead[DEHASH(dev, dirclust + diroffset)];
120loop:
121 for (ldep = deh->deh_chain[0]; ldep != (struct denode *) deh;
122 ldep = ldep->de_forw) {
123 if (dev != ldep->de_dev || ldep->de_refcnt == 0)
124 continue;
125 if (dirclust != ldep->de_dirclust
126 || diroffset != ldep->de_diroffset)
127 continue;
128 if (ldep->de_flag & DELOCKED) {
129 /*
130 * should we brelse() the passed buf hdr to avoid
131 * some potential deadlock?
132 */
133 ldep->de_flag |= DEWANT;
134 sleep((caddr_t) ldep, PINOD);
135 goto loop;
136 }
137 if (vget(DETOV(ldep)))
138 goto loop;
139 *depp = ldep;
140 return 0;
141 }
142
143
144 /*
145 * Directory entry was not in cache, have to create a vnode and
146 * copy it from the passed disk buffer.
147 */
148 /* getnewvnode() does a VREF() on the vnode */
149 if (error = getnewvnode(VT_MSDOSFS, mntp, &msdosfs_vnodeops, &nvp)) {
150 *depp = 0;
151 return error;
152 }
153 ldep = VTODE(nvp);
154 ldep->de_vnode = nvp;
155 ldep->de_flag = 0;
156 ldep->de_devvp = 0;
157 ldep->de_lockf = 0;
158 ldep->de_dev = dev;
159 fc_purge(ldep, 0); /* init the fat cache for this denode */
160
161 /*
162 * Insert the denode into the hash queue and lock the denode so it
163 * can't be accessed until we've read it in and have done what we
164 * need to it.
165 */
166 insque(ldep, deh);
167 DELOCK(ldep);
168
169 /*
170 * Copy the directory entry into the denode area of the vnode.
171 */
172 if (dirclust == MSDOSFSROOT && diroffset == MSDOSFSROOT_OFS) {
173 /*
174 * Directory entry for the root directory. There isn't one,
175 * so we manufacture one. We should probably rummage
176 * through the root directory and find a label entry (if it
177 * exists), and then use the time and date from that entry
178 * as the time and date for the root denode.
179 */
180 ldep->de_Attributes = ATTR_DIRECTORY;
181 ldep->de_StartCluster = MSDOSFSROOT;
182 ldep->de_FileSize = pmp->pm_rootdirsize * pmp->pm_BytesPerSec;
183 /*
184 * fill in time and date so that dos2unixtime() doesn't
185 * spit up when called from msdosfs_getattr() with root
186 * denode
187 */
188 ldep->de_Time = 0x0000; /* 00:00:00 */
189 ldep->de_Date = (0 << 9) | (1 << 5) | (1 << 0);
190 /* Jan 1, 1980 */
191 /* leave the other fields as garbage */
192 }
193 else {
194 bp = NULL;
195 if (!direntptr) {
196 error = readep(pmp, dirclust, diroffset, &bp,
197 &direntptr);
198 if (error)
199 return error;
200 }
201 ldep->de_de = *direntptr;
202 if (bp)
203 brelse(bp);
204 }
205
206 /*
207 * Fill in a few fields of the vnode and finish filling in the
208 * denode. Then return the address of the found denode.
209 */
210 ldep->de_pmp = pmp;
211 ldep->de_devvp = pmp->pm_devvp;
212 ldep->de_refcnt = 1;
213 ldep->de_dirclust = dirclust;
214 ldep->de_diroffset = diroffset;
215 if (ldep->de_Attributes & ATTR_DIRECTORY) {
216 /*
217 * Since DOS directory entries that describe directories
218 * have 0 in the filesize field, we take this opportunity
219 * to find out the length of the directory and plug it into
220 * the denode structure.
221 */
222 u_long size;
223
224 nvp->v_type = VDIR;
225 if (ldep->de_StartCluster == MSDOSFSROOT)
226 nvp->v_flag |= VROOT;
227 else {
228 error = pcbmap(ldep, 0xffff, 0, &size);
229 if (error == E2BIG) {
230 ldep->de_FileSize = size << pmp->pm_cnshift;
231 error = 0;
232 }
233 else
234 printf("deget(): pcbmap returned %d\n", error);
235 }
236 }
237 else
238 nvp->v_type = VREG;
239 VREF(ldep->de_devvp);
240 *depp = ldep;
241 return 0;
242}
243
244void
245deput(dep)
246 struct denode *dep;
247{
248 if ((dep->de_flag & DELOCKED) == 0)
249 panic("deput: denode not locked");
250 DEUNLOCK(dep);
251 vrele(DETOV(dep));
252}
253
254int
255deupdat(dep, tp, waitfor)
256 struct denode *dep;
257 struct timeval *tp;
258 int waitfor;
259{
260 int error;
261 daddr_t bn;
262 int diro;
263 struct buf *bp;
264 struct direntry *dirp;
265 struct msdosfsmount *pmp = dep->de_pmp;
266 struct vnode *vp = DETOV(dep);
267
268#if defined(MSDOSFSDEBUG)
269 printf("deupdat(): dep %08x\n", dep);
270#endif /* defined(MSDOSFSDEBUG) */
271
272 /*
273 * If the update bit is off, or this denode is from a readonly
274 * filesystem, or this denode is for a directory, or the denode
275 * represents an open but unlinked file then don't do anything. DOS
276 * directory entries that describe a directory do not ever get
277 * updated. This is the way dos treats them.
278 */
279 if ((dep->de_flag & DEUPD) == 0 ||
280 vp->v_mount->mnt_flag & MNT_RDONLY ||
281 dep->de_Attributes & ATTR_DIRECTORY ||
282 dep->de_refcnt <= 0)
283 return 0;
284
285 /*
286 * Read in the cluster containing the directory entry we want to
287 * update.
288 */
289 if (error = readde(dep, &bp, &dirp))
290 return error;
291
292 /*
293 * Put the passed in time into the directory entry.
294 */
295 unix2dostime(&time, (union dosdate *) & dep->de_Date,
296 (union dostime *) & dep->de_Time);
297 dep->de_flag &= ~DEUPD;
298
299 /*
300 * Copy the directory entry out of the denode into the cluster it
301 * came from.
302 */
303 *dirp = dep->de_de; /* structure copy */
304
305 /*
306 * Write the cluster back to disk. If they asked for us to wait
307 * for the write to complete, then use bwrite() otherwise use
308 * bdwrite().
309 */
310 error = 0; /* note that error is 0 from above, but ... */
311 if (waitfor)
312 error = bwrite(bp);
313 else
314 bdwrite(bp);
315 return error;
316}
317
318/*
319 * Truncate the file described by dep to the length specified by length.
320 */
321int
322detrunc(dep, length, flags)
323 struct denode *dep;
324 u_long length;
325 int flags;
326{
327 int error;
328 int allerror;
329 u_long eofentry;
330 u_long chaintofree;
331 daddr_t bn;
332 int boff;
333 int isadir = dep->de_Attributes & ATTR_DIRECTORY;
334 struct buf *bp;
335 struct msdosfsmount *pmp = dep->de_pmp;
336
337#if defined(MSDOSFSDEBUG)
338 printf("detrunc(): file %s, length %d, flags %d\n", dep->de_Name, length, flags);
339#endif /* defined(MSDOSFSDEBUG) */
340
341 /*
342 * Disallow attempts to truncate the root directory since it is of
343 * fixed size. That's just the way dos filesystems are. We use
344 * the VROOT bit in the vnode because checking for the directory
345 * bit and a startcluster of 0 in the denode is not adequate to
346 * recognize the root directory at this point in a file or
347 * directory's life.
348 */
349 if (DETOV(dep)->v_flag & VROOT) {
350 printf("detrunc(): can't truncate root directory, clust %d, offset %d\n",
351 dep->de_dirclust, dep->de_diroffset);
352 return EINVAL;
353 }
354
355 vnode_pager_setsize(DETOV(dep), length);
356
357 if (dep->de_FileSize <= length) {
358 dep->de_flag |= DEUPD;
359 error = deupdat(dep, &time, 1);
360#if defined(MSDOSFSDEBUG)
361 printf("detrunc(): file is shorter return point, errno %d\n", error);
362#endif /* defined(MSDOSFSDEBUG) */
363 return error;
364 }
365
366 /*
367 * If the desired length is 0 then remember the starting cluster of
368 * the file and set the StartCluster field in the directory entry
369 * to 0. If the desired length is not zero, then get the number of
370 * the last cluster in the shortened file. Then get the number of
371 * the first cluster in the part of the file that is to be freed.
372 * Then set the next cluster pointer in the last cluster of the
373 * file to CLUST_EOFE.
374 */
375 if (length == 0) {
376 chaintofree = dep->de_StartCluster;
377 dep->de_StartCluster = 0;
378 eofentry = ~0;
379 }
380 else {
381 error = pcbmap(dep, (length - 1) >> pmp->pm_cnshift,
382 0, &eofentry);
383 if (error) {
384#if defined(MSDOSFSDEBUG)
385 printf("detrunc(): pcbmap fails %d\n", error);
386#endif /* defined(MSDOSFSDEBUG) */
387 return error;
388 }
389 }
390
391 fc_purge(dep, (length + pmp->pm_crbomask) >> pmp->pm_cnshift);
392
393 /*
394 * If the new length is not a multiple of the cluster size then we
395 * must zero the tail end of the new last cluster in case it
396 * becomes part of the file again because of a seek.
397 */
398 if ((boff = length & pmp->pm_crbomask) != 0) {
399 /*
400 * should read from file vnode or filesystem vnode
401 * depending on if file or dir
402 */
403 if (isadir) {
404 bn = cntobn(pmp, eofentry);
405 error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster,
406 NOCRED, &bp);
407 }
408 else {
409 bn = (length - 1) >> pmp->pm_cnshift;
410 error = bread(DETOV(dep), bn, pmp->pm_bpcluster,
411 NOCRED, &bp);
412 }
413 if (error) {
414#if defined(MSDOSFSDEBUG)
415 printf("detrunc(): bread fails %d\n", error);
416#endif /* defined(MSDOSFSDEBUG) */
417 return error;
418 }
419 vnode_pager_uncache(DETOV(dep)); /* what's this for? */
420 /*
421 * is this the right place for it?
422 */
423 bzero(bp->b_un.b_addr + boff, pmp->pm_bpcluster - boff);
424 if (flags & IO_SYNC)
425 bwrite(bp);
426 else
427 bdwrite(bp);
428 }
429
430 /*
431 * Write out the updated directory entry. Even if the update fails
432 * we free the trailing clusters.
433 */
434 dep->de_FileSize = length;
435 dep->de_flag |= DEUPD;
436 vinvalbuf(DETOV(dep), length > 0);
437 allerror = deupdat(dep, &time, MNT_WAIT);
438#if defined(MSDOSFSDEBUG)
439 printf("detrunc(): allerror %d, eofentry %d\n",
440 allerror, eofentry);
441#endif /* defined(MSDOSFSDEBUG) */
442
443 /*
444 * If we need to break the cluster chain for the file then do it
445 * now.
446 */
447 if (eofentry != ~0) {
448 error = fatentry(FAT_GET_AND_SET, pmp, eofentry,
449 &chaintofree, CLUST_EOFE);
450 if (error) {
451#if defined(MSDOSFSDEBUG)
452 printf("detrunc(): fatentry errors %d\n", error);
453#endif /* defined(MSDOSFSDEBUG) */
454 return error;
455 }
456 fc_setcache(dep, FC_LASTFC, (length - 1) >> pmp->pm_cnshift,
457 eofentry);
458 }
459
460 /*
461 * Now free the clusters removed from the file because of the
462 * truncation.
463 */
464 if (chaintofree != 0 && !MSDOSFSEOF(chaintofree))
465 freeclusterchain(pmp, chaintofree);
466
467 return allerror;
468}
469
470/*
471 * Move a denode to its correct hash queue after the file it represents has
472 * been moved to a new directory.
473 */
474reinsert(dep)
475 struct denode *dep;
476{
477 struct msdosfsmount *pmp = dep->de_pmp;
478 union dehead *deh;
479
480 /*
481 * Fix up the denode cache. If the denode is for a directory,
482 * there is nothing to do since the hash is based on the starting
483 * cluster of the directory file and that hasn't changed. If for a
484 * file the hash is based on the location of the directory entry,
485 * so we must remove it from the cache and re-enter it with the
486 * hash based on the new location of the directory entry.
487 */
488 if ((dep->de_Attributes & ATTR_DIRECTORY) == 0) {
489 remque(dep);
490 deh = &dehead[DEHASH(pmp->pm_dev,
491 dep->de_dirclust + dep->de_diroffset)];
492 insque(dep, deh);
493 }
494}
495
496int msdosfs_prtactive; /* print reclaims of active vnodes */
497
498int
499msdosfs_reclaim(vp)
500 struct vnode *vp;
501{
502 struct denode *dep = VTODE(vp);
503 int i;
504
505#if defined(MSDOSFSDEBUG)
506 printf("msdosfs_reclaim(): dep %08x, file %s, refcnt %d\n",
507 dep, dep->de_Name, dep->de_refcnt);
508#endif /* defined(MSDOSFSDEBUG) */
509
510 if (msdosfs_prtactive && vp->v_usecount != 0)
511 vprint("msdosfs_reclaim(): pushing active", vp);
512
513 /*
514 * Remove the denode from the denode hash chain we are in.
515 */
516 remque(dep);
517 dep->de_forw = dep;
518 dep->de_back = dep;
519
520 cache_purge(vp);
521 /*
522 * Indicate that one less file on the filesystem is open.
523 */
524 if (dep->de_devvp) {
525 vrele(dep->de_devvp);
526 dep->de_devvp = 0;
527 }
528
529 dep->de_flag = 0;
530 return 0;
531}
532
533int
534msdosfs_inactive(vp, p)
535 struct vnode *vp;
536 struct proc *p;
537{
538 struct denode *dep = VTODE(vp);
539 int error = 0;
540
541#if defined(MSDOSFSDEBUG)
542 printf("msdosfs_inactive(): dep %08x, de_Name[0] %x\n", dep, dep->de_Name[0]);
543#endif /* defined(MSDOSFSDEBUG) */
544
545 if (msdosfs_prtactive && vp->v_usecount != 0)
546 vprint("msdosfs_inactive(): pushing active", vp);
547
548 /*
549 * Get rid of denodes related to stale file handles. Hmmm, what
550 * does this really do?
551 */
552 if (dep->de_Name[0] == SLOT_DELETED) {
553 if ((vp->v_flag & VXLOCK) == 0)
554 vgone(vp);
555 return 0;
556 }
557
558 /*
559 * If the file has been deleted and it is on a read/write
560 * filesystem, then truncate the file, and mark the directory slot
561 * as empty. (This may not be necessary for the dos filesystem.
562 */
563#if defined(MSDOSFSDEBUG)
564 printf("msdosfs_inactive(): dep %08x, refcnt %d, mntflag %x, MNT_RDONLY %x\n",
565 dep, dep->de_refcnt, vp->v_mount->mnt_flag, MNT_RDONLY);
566#endif /* defined(MSDOSFSDEBUG) */
567 DELOCK(dep);
568 if (dep->de_refcnt <= 0 && (vp->v_mount->mnt_flag & MNT_RDONLY) == 0) {
569 error = detrunc(dep, (u_long) 0, 0);
570 dep->de_flag |= DEUPD;
571 dep->de_Name[0] = SLOT_DELETED;
572 }
573 DEUPDAT(dep, &time, 0);
574 DEUNLOCK(dep);
575 dep->de_flag = 0;
576
577 /*
578 * If we are done with the denode, then reclaim it so that it can
579 * be reused now.
580 */
581#if defined(MSDOSFSDEBUG)
582 printf("msdosfs_inactive(): v_usecount %d, de_Name[0] %x\n", vp->v_usecount,
583 dep->de_Name[0]);
584#endif /* defined(MSDOSFSDEBUG) */
585 if (vp->v_usecount == 0 && dep->de_Name[0] == SLOT_DELETED)
586 vgone(vp);
587 return error;
588}
589
590int
591delock(dep)
592 struct denode *dep;
593{
594 while (dep->de_flag & DELOCKED) {
595 dep->de_flag |= DEWANT;
596 if (dep->de_spare0 == curproc->p_pid)
597 panic("delock: locking against myself");
598 dep->de_spare1 = curproc->p_pid;
599 (void) sleep((caddr_t) dep, PINOD);
600 }
601 dep->de_spare1 = 0;
602 dep->de_spare0 = curproc->p_pid;
603 dep->de_flag |= DELOCKED;
604
605 return 0;
606}
607
608int
609deunlock(dep)
610 struct denode *dep;
611{
612 if ((dep->de_flag & DELOCKED) == 0)
613 vprint("deunlock: found unlocked denode", DETOV(dep));
614 dep->de_spare0 = 0;
615 dep->de_flag &= ~DELOCKED;
616 if (dep->de_flag & DEWANT) {
617 dep->de_flag &= ~DEWANT;
618 wakeup((caddr_t) dep);
619 }
620
621 return 0;
622}