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