Commit | Line | Data |
---|---|---|
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 remove this notice. | |
7 | * | |
8 | * This software is provided "as is". | |
9 | * | |
10 | * The author 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 | * | |
3b589918 | 18 | * $Id: pcfs_vnops.c,v 1.6 1994/05/25 08:33:15 swallace Exp $ |
15637ed4 RG |
19 | */ |
20 | ||
21 | #include "param.h" | |
22 | #include "systm.h" | |
23 | #include "namei.h" | |
24 | #include "resourcevar.h" /* defines plimit structure in proc struct */ | |
25 | #include "kernel.h" | |
26 | #include "file.h" /* define FWRITE ... */ | |
27 | #include "stat.h" | |
28 | #include "buf.h" | |
29 | #include "proc.h" | |
30 | #include "mount.h" | |
31 | #include "vnode.h" | |
32 | #include "specdev.h" /* defines plimit structure in the proc struct */ | |
33 | #include "malloc.h" | |
34 | ||
35 | #include "dir.h" /* defines dirent structure */ | |
36 | ||
37 | #include "bpb.h" | |
38 | #include "direntry.h" | |
39 | #include "denode.h" | |
40 | #include "pcfsmount.h" | |
41 | #include "fat.h" | |
42 | /* | |
43 | * Some general notes: | |
44 | * | |
45 | * In the ufs filesystem the inodes, superblocks, and indirect | |
46 | * blocks are read/written using the vnode for the filesystem. | |
47 | * Blocks that represent the contents of a file are read/written | |
48 | * using the vnode for the file (including directories when | |
49 | * they are read/written as files). | |
50 | * This presents problems for the dos filesystem because data | |
51 | * that should be in an inode (if dos had them) resides in the | |
52 | * directory itself. Since we must update directory entries | |
53 | * without the benefit of having the vnode for the directory | |
54 | * we must use the vnode for the filesystem. This means that | |
55 | * when a directory is actually read/written (via read, write, | |
56 | * or readdir, or seek) we must use the vnode for the filesystem | |
57 | * instead of the vnode for the directory as would happen in ufs. | |
58 | * This is to insure we retreive the correct block from the | |
59 | * buffer cache since the hash value is based upon the vnode | |
60 | * address and the desired block number. | |
61 | */ | |
62 | ||
63 | /* | |
64 | * Create a regular file. | |
65 | * On entry the directory to contain the file being | |
66 | * created is locked. We must release before we | |
67 | * return. | |
68 | * We must also free the pathname buffer pointed at | |
69 | * by ndp->ni_pnbuf, always on error, or only if the | |
70 | * SAVESTART bit in ni_nameiop is clear on success. | |
71 | */ | |
72 | int | |
73 | pcfs_create(ndp, vap, p) | |
74 | struct nameidata *ndp; | |
75 | struct vattr *vap; | |
76 | struct proc *p; | |
77 | { | |
78 | struct denode ndirent; | |
79 | struct direntry *ndirp = &ndirent.de_de; | |
80 | struct denode *dep; | |
81 | struct denode *pdep = VTODE(ndp->ni_dvp); | |
82 | int error; | |
83 | #if defined(PCFSDEBUG) | |
84 | printf("pcfs_create(ndp %08x, vap %08x, p %08x\n", ndp, vap, p); | |
85 | #endif /* defined(PCFSDEBUG) */ | |
86 | ||
87 | /* | |
88 | * Create a directory entry for the file, then call | |
89 | * createde() to have it installed. | |
90 | * NOTE: DOS files are always executable. We use the | |
91 | * absence of the owner write bit to make the file readonly. | |
92 | */ | |
93 | bzero(&ndirent, sizeof(ndirent)); | |
94 | unix2dostime(&time, (union dosdate *)&ndirp->deDate, | |
95 | (union dostime *)&ndirp->deTime); | |
96 | unix2dosfn((u_char *)ndp->ni_ptr, ndirp->deName, ndp->ni_namelen); | |
97 | ndirp->deAttributes = (vap->va_mode & VWRITE) ? 0 : ATTR_READONLY; | |
c8197bda AC |
98 | if (vap->va_mode & VEXEC) |
99 | ndirp->deAttributes |= ATTR_HIDDEN; | |
15637ed4 RG |
100 | ndirp->deStartCluster = 0; |
101 | ndirp->deFileSize = 0; | |
102 | ndirent.de_pmp = pdep->de_pmp; | |
103 | ndirent.de_dev = pdep->de_dev; | |
104 | ndirent.de_devvp = pdep->de_devvp; | |
105 | if ((error = createde(&ndirent, ndp, &dep)) == 0) { | |
106 | ndp->ni_vp = DETOV(dep); | |
107 | if ((ndp->ni_nameiop & SAVESTART) == 0) | |
108 | free(ndp->ni_pnbuf, M_NAMEI); | |
109 | } else { | |
110 | free(ndp->ni_pnbuf, M_NAMEI); | |
111 | } | |
112 | deput(pdep); /* release parent dir */ | |
113 | return error; | |
114 | } | |
115 | ||
116 | int | |
117 | pcfs_mknod(ndp, vap, cred, p) | |
118 | struct nameidata *ndp; | |
119 | struct vattr *vap; | |
120 | struct ucred *cred; | |
121 | struct proc *p; | |
122 | { | |
123 | int error; | |
124 | ||
125 | switch (vap->va_type) { | |
126 | case VDIR: | |
127 | error = pcfs_mkdir(ndp, vap, p); | |
128 | break; | |
129 | ||
130 | /* | |
131 | * pcfs_create() sets ndp->ni_vp. | |
132 | */ | |
133 | case VREG: | |
134 | error = pcfs_create(ndp, vap, p); | |
135 | break; | |
136 | ||
137 | default: | |
138 | error = EINVAL; | |
139 | break; | |
140 | } | |
141 | return error; | |
142 | } | |
143 | ||
144 | int | |
145 | pcfs_open(vp, mode, cred, p) | |
146 | struct vnode *vp; | |
147 | int mode; | |
148 | struct ucred *cred; | |
149 | struct proc *p; | |
150 | { | |
151 | return 0; | |
152 | } | |
153 | ||
154 | int | |
155 | pcfs_close(vp, fflag, cred, p) | |
156 | struct vnode *vp; | |
157 | int fflag; | |
158 | struct ucred *cred; | |
159 | struct proc *p; | |
160 | { | |
161 | struct denode *dep = VTODE(vp); | |
162 | ||
163 | if (vp->v_usecount > 1 && !(dep->de_flag & DELOCKED)) | |
164 | DETIMES(dep, &time); | |
165 | return 0; | |
166 | } | |
167 | ||
168 | int | |
169 | pcfs_access(vp, mode, cred, p) | |
170 | struct vnode *vp; | |
171 | int mode; | |
172 | struct ucred *cred; | |
173 | struct proc *p; | |
174 | { | |
175 | int dosmode; | |
176 | struct denode *dep = VTODE(vp); | |
177 | ||
178 | /* | |
179 | * Root gets to do anything. Even execute a file | |
180 | * without the x-bit on? But, for dos filesystems | |
181 | * every file is executable. I may regret this. | |
182 | */ | |
183 | if (cred->cr_uid == 0) | |
184 | return 0; | |
185 | ||
186 | /* | |
187 | * mode is filled with a combination of VREAD, VWRITE, | |
188 | * and/or VEXEC bits turned on. In an octal number these | |
189 | * are the Y in 0Y00. | |
190 | * | |
191 | * Since the dos filesystem doesn't have the concept of | |
192 | * file ownership we just give everybody read and execute | |
193 | * access and write access if the readonly bit is off. | |
194 | */ | |
195 | dosmode = VEXEC | VREAD | | |
196 | ((dep->de_Attributes & ATTR_READONLY) ? 0 : VWRITE); | |
197 | return ((dosmode & mode) != 0) ? 0 : EACCES; | |
198 | } | |
199 | ||
200 | int | |
201 | pcfs_getattr(vp, vap, cred, p) | |
202 | struct vnode *vp; | |
203 | struct vattr *vap; | |
204 | struct ucred *cred; | |
205 | struct proc *p; | |
206 | { | |
207 | u_int cn; | |
208 | struct denode *dep = VTODE(vp); | |
209 | ||
210 | DETIMES(dep, &time); | |
211 | vap->va_fsid = dep->de_dev; | |
212 | /* The following computation of the fileid must be the | |
213 | * same as that used in pcfs_readdir() to compute d_fileno. | |
214 | * If not, pwd doesn't work. */ | |
215 | if (dep->de_Attributes & ATTR_DIRECTORY) { | |
216 | if ((cn = dep->de_StartCluster) == PCFSROOT) | |
217 | cn = 1; | |
218 | } else { | |
219 | if ((cn = dep->de_dirclust) == PCFSROOT) | |
220 | cn = 1; | |
221 | cn = (cn << 16) | (dep->de_diroffset & 0xffff); | |
222 | } | |
223 | vap->va_fileid = cn; | |
c8197bda AC |
224 | vap->va_mode = (dep->de_Attributes & ATTR_READONLY) ? 0444 : 0666; |
225 | if ( dep->de_Attributes & ATTR_HIDDEN | |
226 | || dep->de_Attributes & ATTR_DIRECTORY | |
227 | ) | |
228 | vap->va_mode |= 0111; | |
15637ed4 RG |
229 | if (dep->de_Attributes & ATTR_DIRECTORY) |
230 | vap->va_mode |= S_IFDIR; | |
231 | vap->va_nlink = 1; | |
232 | vap->va_gid = 0; | |
233 | vap->va_uid = 0; | |
234 | vap->va_rdev = 0; | |
235 | vap->va_size = dep->de_FileSize; | |
236 | vap->va_size_rsv = 0; | |
237 | dos2unixtime((union dosdate *)&dep->de_Date, | |
238 | (union dostime *)&dep->de_Time, &vap->va_atime); | |
239 | vap->va_atime.tv_usec = 0; | |
240 | vap->va_mtime.tv_sec = vap->va_atime.tv_sec; | |
241 | vap->va_mtime.tv_usec = 0; | |
242 | vap->va_ctime.tv_sec = vap->va_atime.tv_sec; | |
243 | vap->va_ctime.tv_usec = 0; | |
244 | vap->va_flags = dep->de_flag; | |
245 | vap->va_gen = 0; | |
246 | vap->va_blocksize = dep->de_pmp->pm_bpcluster; | |
247 | vap->va_bytes = (dep->de_FileSize + dep->de_pmp->pm_crbomask) & | |
248 | ~(dep->de_pmp->pm_crbomask); | |
249 | vap->va_bytes_rsv = 0; | |
250 | vap->va_type = vp->v_type; | |
251 | return 0; | |
252 | } | |
253 | ||
254 | int | |
255 | pcfs_setattr(vp, vap, cred, p) | |
256 | struct vnode *vp; | |
257 | struct vattr *vap; | |
258 | struct ucred *cred; | |
259 | struct proc *p; | |
260 | { | |
261 | int error = 0; | |
262 | struct denode *dep = VTODE(vp); | |
263 | ||
264 | #if defined(PCFSDEBUG) | |
265 | printf("pcfs_setattr(): vp %08x, vap %08x, cred %08x, p %08x\n", | |
266 | vp, vap, cred, p); | |
267 | #endif /* defined(PCFSDEBUG) */ | |
268 | if ((vap->va_type != VNON) || | |
269 | (vap->va_nlink != VNOVAL) || | |
270 | (vap->va_fsid != VNOVAL) || | |
271 | (vap->va_fileid != VNOVAL) || | |
272 | (vap->va_blocksize != VNOVAL) || | |
273 | (vap->va_rdev != VNOVAL) || | |
274 | (vap->va_bytes != VNOVAL) || | |
275 | (vap->va_gen != VNOVAL) || | |
276 | (vap->va_uid != (u_short)VNOVAL) || | |
277 | (vap->va_gid != (u_short)VNOVAL) || | |
278 | (vap->va_atime.tv_sec != VNOVAL)) { | |
279 | #if defined(PCFSDEBUG) | |
280 | printf("pcfs_setattr(): returning EINVAL\n"); | |
281 | printf(" va_type %d, va_nlink %x, va_fsid %x, va_fileid %x\n", | |
282 | vap->va_type, vap->va_nlink, vap->va_fsid, vap->va_fileid); | |
283 | printf(" va_blocksize %x, va_rdev %x, va_bytes %x, va_gen %x\n", | |
284 | vap->va_blocksize, vap->va_rdev, vap->va_bytes, vap->va_gen); | |
285 | printf(" va_uid %x, va_gid %x, va_atime.tv_sec %x\n", | |
286 | vap->va_uid, vap->va_gid, vap->va_atime.tv_sec); | |
287 | #endif /* defined(PCFSDEBUG) */ | |
288 | return EINVAL; | |
289 | } | |
290 | ||
291 | if (vap->va_size != VNOVAL) { | |
292 | if (vp->v_type == VDIR) | |
293 | return EISDIR; | |
294 | if (error = detrunc(dep, vap->va_size, 0)) | |
295 | return error; | |
296 | } | |
297 | if (vap->va_mtime.tv_sec != VNOVAL) { | |
298 | dep->de_flag |= DEUPD; | |
299 | if (error = deupdat(dep, &vap->va_mtime, 1)) | |
300 | return error; | |
301 | } | |
302 | ||
303 | /* | |
304 | * DOS files only have the ability to have thier | |
305 | * writability attribute set, so we use the owner | |
306 | * write bit to set the readonly attribute. | |
307 | */ | |
308 | if (vap->va_mode != (u_short)VNOVAL) { | |
c8197bda AC |
309 | /* We ignore the read bits */ |
310 | if (vap->va_mode & VEXEC && vp->v_type != VDIR) | |
311 | dep->de_Attributes |= ATTR_HIDDEN; | |
312 | else | |
313 | dep->de_Attributes &= ~ATTR_HIDDEN; | |
15637ed4 RG |
314 | if (vap->va_mode & VWRITE) |
315 | dep->de_Attributes &= ~ATTR_READONLY; | |
316 | else | |
317 | dep->de_Attributes |= ATTR_READONLY; | |
318 | dep->de_flag |= DEUPD; | |
319 | } | |
320 | ||
321 | if (vap->va_flags != VNOVAL) { | |
322 | if (error = suser(cred, &p->p_acflag)) | |
323 | return error; | |
324 | if (cred->cr_uid == 0) | |
325 | dep->de_flag = vap->va_flags; | |
326 | else { | |
fde1aeb2 | 327 | dep->de_flag &= 0xffff0000UL; |
15637ed4 RG |
328 | dep->de_flag |= (vap->va_flags & 0xffff); |
329 | } | |
330 | dep->de_flag |= DEUPD; | |
331 | } | |
332 | return error; | |
333 | } | |
334 | ||
335 | ||
336 | int | |
337 | pcfs_read(vp, uio, ioflag, cred) | |
338 | struct vnode *vp; | |
339 | struct uio *uio; | |
340 | int ioflag; | |
341 | struct ucred *cred; | |
342 | { | |
343 | int error = 0; | |
344 | int diff; | |
345 | int isadir; | |
346 | long n; | |
347 | long on; | |
348 | daddr_t bn; | |
349 | daddr_t lbn; | |
350 | daddr_t rablock; | |
351 | struct buf *bp; | |
352 | struct denode *dep = VTODE(vp); | |
353 | struct pcfsmount *pmp = dep->de_pmp; | |
354 | ||
355 | /* | |
356 | * If they didn't ask for any data, then we | |
357 | * are done. | |
358 | */ | |
359 | if (uio->uio_resid == 0) | |
360 | return 0; | |
361 | if (uio->uio_offset < 0) | |
362 | return EINVAL; | |
363 | ||
364 | isadir = dep->de_Attributes & ATTR_DIRECTORY; | |
365 | do { | |
366 | lbn = uio->uio_offset >> pmp->pm_cnshift; | |
367 | on = uio->uio_offset & pmp->pm_crbomask; | |
368 | n = MIN((u_long)(pmp->pm_bpcluster - on), uio->uio_resid); | |
369 | diff = dep->de_FileSize - uio->uio_offset; | |
370 | if (diff <= 0) | |
371 | return 0; | |
372 | /* convert cluster # to block # if a directory */ | |
373 | if (isadir) { | |
374 | error = pcbmap(dep, lbn, &lbn, 0); | |
375 | if (error) | |
376 | return error; | |
377 | } | |
378 | if (diff < n) | |
379 | n = diff; | |
380 | /* | |
381 | * If we are operating on a directory file then be | |
382 | * sure to do i/o with the vnode for the filesystem | |
383 | * instead of the vnode for the directory. | |
384 | */ | |
385 | if (isadir) { | |
386 | error = bread(pmp->pm_devvp, lbn, pmp->pm_bpcluster, | |
387 | NOCRED, &bp); | |
388 | } else { | |
389 | rablock = lbn + 1; | |
390 | if (vp->v_lastr + 1 == lbn && | |
391 | rablock * pmp->pm_bpcluster < dep->de_FileSize) { | |
392 | error = breada(vp, lbn, pmp->pm_bpcluster, | |
393 | rablock, pmp->pm_bpcluster, NOCRED, &bp); | |
394 | } else { | |
395 | error = bread(vp, lbn, pmp->pm_bpcluster, NOCRED, | |
396 | &bp); | |
397 | } | |
398 | vp->v_lastr = lbn; | |
399 | } | |
400 | n = MIN(n, pmp->pm_bpcluster - bp->b_resid); | |
401 | if (error) { | |
402 | brelse(bp); | |
403 | return error; | |
404 | } | |
405 | error = uiomove(bp->b_un.b_addr + on, (int)n, uio); | |
15637ed4 RG |
406 | brelse(bp); |
407 | } while (error == 0 && uio->uio_resid > 0 && n != 0); | |
408 | return error; | |
409 | } | |
410 | ||
411 | /* | |
412 | * Write data to a file or directory. | |
413 | */ | |
414 | int | |
415 | pcfs_write(vp, uio, ioflag, cred) | |
416 | struct vnode *vp; | |
417 | struct uio *uio; | |
418 | int ioflag; | |
419 | struct ucred *cred; | |
420 | { | |
421 | int n; | |
422 | int isadir; | |
423 | int croffset; | |
424 | int resid; | |
425 | int osize; | |
426 | int error; | |
427 | u_long cluster; | |
428 | u_long nextcluster; | |
429 | u_long lastcluster; | |
430 | daddr_t bn; | |
431 | struct buf *bp; | |
432 | struct proc *p = uio->uio_procp; | |
433 | struct vnode *thisvp; | |
434 | struct denode *dep = VTODE(vp); | |
435 | struct pcfsmount *pmp = dep->de_pmp; | |
436 | ||
437 | #if defined(PCFSDEBUG) | |
438 | printf("pcfs_write(vp %08x, uio %08x, ioflag %08x, cred %08x\n", | |
439 | vp, uio, ioflag, cred); | |
440 | printf("pcfs_write(): diroff %d, dirclust %d, startcluster %d\n", | |
441 | dep->de_diroffset, dep->de_dirclust, dep->de_StartCluster); | |
442 | #endif /* defined(PCFSDEBUG) */ | |
443 | ||
444 | switch (vp->v_type) { | |
445 | case VREG: | |
446 | if (ioflag & IO_APPEND) | |
447 | uio->uio_offset = dep->de_FileSize; | |
448 | isadir = 0; | |
449 | thisvp = vp; | |
450 | break; | |
451 | ||
452 | case VDIR: | |
453 | if ((ioflag & IO_SYNC) == 0) | |
454 | panic("pcfs_write(): non-sync directory update"); | |
455 | isadir = 1; | |
456 | thisvp = pmp->pm_devvp; | |
457 | break; | |
458 | ||
459 | default: | |
460 | panic("pcfs_write(): bad file type"); | |
461 | break; | |
462 | } | |
463 | ||
464 | if (uio->uio_offset < 0) { | |
465 | return EINVAL; | |
466 | } | |
467 | if (uio->uio_resid == 0) | |
468 | return 0; | |
469 | ||
470 | /* | |
471 | * If they've exceeded their filesize limit, tell them about it. | |
472 | */ | |
473 | if (vp->v_type == VREG && p && | |
474 | ((uio->uio_offset + uio->uio_resid) > | |
475 | p->p_rlimit[RLIMIT_FSIZE].rlim_cur)) { | |
476 | psignal(p, SIGXFSZ); | |
477 | return EFBIG; | |
478 | } | |
479 | ||
480 | /* | |
481 | * If attempting to write beyond the end of the root | |
482 | * directory we stop that here because the root directory | |
483 | * can not grow. | |
484 | */ | |
485 | if ((dep->de_Attributes & ATTR_DIRECTORY) && | |
486 | dep->de_StartCluster == PCFSROOT && | |
487 | (uio->uio_offset+uio->uio_resid) > dep->de_FileSize) | |
488 | return ENOSPC; | |
489 | ||
490 | /* | |
491 | * If the offset we are starting the write at is beyond the | |
492 | * end of the file, then they've done a seek. Unix filesystems | |
493 | * allow files with holes in them, DOS doesn't so we must | |
494 | * fill the hole with zeroed blocks. We do this by calling | |
495 | * our seek function. This could probably be cleaned up | |
496 | * someday. | |
497 | */ | |
498 | if (uio->uio_offset > dep->de_FileSize) { | |
499 | error = pcfs_seek(vp, (off_t)0, uio->uio_offset, cred); | |
500 | if (error) | |
501 | return error; | |
502 | } | |
503 | ||
504 | /* | |
505 | * Remember some values in case the write fails. | |
506 | */ | |
507 | resid = uio->uio_resid; | |
508 | osize = dep->de_FileSize; | |
509 | ||
510 | do { | |
511 | bn = uio->uio_offset >> pmp->pm_cnshift; | |
512 | /* | |
513 | * If we are appending to the file and we are on a | |
514 | * cluster boundary, then allocate a new cluster | |
515 | * and chain it onto the file. | |
516 | */ | |
517 | if (uio->uio_offset == dep->de_FileSize && | |
518 | (uio->uio_offset & pmp->pm_crbomask) == 0) { | |
519 | if (error = extendfile(dep, &bp, 0)) | |
520 | break; | |
521 | } else { | |
522 | /* | |
523 | * The block we need to write into exists, | |
524 | * so just read it in. | |
525 | */ | |
526 | if (isadir) { | |
527 | error = pcbmap(dep, bn, &bn, 0); | |
528 | if (error) | |
529 | return error; | |
530 | } | |
531 | error = bread(thisvp, bn, pmp->pm_bpcluster, cred, &bp); | |
532 | if (error) | |
533 | return error; | |
534 | } | |
535 | croffset = uio->uio_offset & pmp->pm_crbomask; | |
536 | n = MIN(uio->uio_resid, pmp->pm_bpcluster-croffset); | |
537 | if (uio->uio_offset+n > dep->de_FileSize) { | |
538 | dep->de_FileSize = uio->uio_offset + n; | |
539 | vnode_pager_setsize(vp, dep->de_FileSize); /* why? */ | |
540 | } | |
541 | (void) vnode_pager_uncache(vp); /* why not? */ | |
542 | /* Should these vnode_pager_* functions be done on dir files? */ | |
543 | ||
544 | /* | |
545 | * Copy the data from user space into the buf header. | |
546 | */ | |
547 | error = uiomove(bp->b_un.b_addr+croffset, n, uio); | |
548 | ||
549 | /* | |
550 | * If they want this synchronous then write it and wait | |
551 | * for it. Otherwise, if on a cluster boundary write it | |
552 | * asynchronously so we can move on to the next block | |
553 | * without delay. Otherwise do a delayed write because | |
554 | * we may want to write somemore into the block later. | |
555 | */ | |
556 | if (ioflag & IO_SYNC) | |
557 | (void) bwrite(bp); | |
558 | else | |
559 | if (n + croffset == pmp->pm_bpcluster) { | |
15637ed4 RG |
560 | bawrite(bp); |
561 | } else | |
562 | bdwrite(bp); | |
563 | dep->de_flag |= DEUPD; | |
564 | } while (error == 0 && uio->uio_resid > 0); | |
565 | ||
566 | /* | |
567 | * If the write failed and they want us to, truncate | |
568 | * the file back to the size it was before the write | |
569 | * was attempted. | |
570 | */ | |
571 | if (error && (ioflag & IO_UNIT)) { | |
572 | detrunc(dep, osize, ioflag & IO_SYNC); | |
573 | uio->uio_offset -= resid - uio->uio_resid; | |
574 | uio->uio_resid = resid; | |
575 | } | |
576 | if (!error && (ioflag & IO_UNIT)) | |
577 | error = deupdat(dep, &time, 1); | |
578 | return error; | |
579 | } | |
580 | ||
581 | int | |
582 | pcfs_ioctl(vp, com, data, fflag, cred, p) | |
583 | struct vnode *vp; | |
584 | int com; | |
585 | caddr_t data; | |
4c45483e | 586 | int fflag; |
15637ed4 RG |
587 | struct ucred *cred; |
588 | struct proc *p; | |
589 | { | |
590 | return ENOTTY; | |
591 | } | |
592 | ||
593 | int | |
594 | pcfs_select(vp, which, fflags, cred, p) | |
595 | struct vnode *vp; | |
596 | int which; | |
597 | int fflags; | |
598 | struct ucred *cred; | |
599 | struct proc *p; | |
600 | { | |
601 | return 1; /* DOS filesystems never block? */ | |
602 | } | |
603 | ||
604 | int | |
605 | pcfs_mmap(vp, fflags, cred, p) | |
606 | struct vnode *vp; | |
607 | int fflags; | |
608 | struct ucred *cred; | |
609 | struct proc *p; | |
610 | { | |
611 | return EINVAL; | |
612 | } | |
613 | ||
614 | /* | |
615 | * Flush the blocks of a file to disk. | |
616 | * | |
617 | * This function is worthless for vnodes that represent | |
618 | * directories. | |
619 | * Maybe we could just do a sync if they try an fsync | |
620 | * on a directory file. | |
621 | */ | |
622 | int | |
623 | pcfs_fsync(vp, fflags, cred, waitfor, p) | |
624 | struct vnode *vp; | |
625 | int fflags; | |
626 | struct ucred *cred; | |
627 | int waitfor; | |
628 | struct proc *p; | |
629 | { | |
630 | struct denode *dep = VTODE(vp); | |
631 | ||
632 | if (fflags & FWRITE) | |
633 | dep->de_flag |= DEUPD; | |
634 | /* | |
635 | * Does this call to vflushbuf() do anything? I can | |
636 | * find no code anywhere that sets v_dirtyblkhd in the | |
637 | * vnode, which vflushbuf() seems to depend upon. | |
638 | */ | |
639 | vflushbuf(vp, waitfor == MNT_WAIT ? B_SYNC : 0); | |
640 | return deupdat(dep, &time, waitfor == MNT_WAIT); | |
641 | } | |
642 | ||
643 | /* | |
644 | * Since the dos filesystem does not allow files with | |
645 | * holes in them we must fill the file with zeroed | |
646 | * blocks when a seek past the end of file happens. | |
647 | * | |
648 | * It seems that nothing in the kernel calls the filesystem | |
649 | * specific file seek functions. And, someone on the | |
650 | * net told me that NFS never sends announcements of | |
651 | * seeks to the server. So, if pcfs ever becomes | |
652 | * NFS mountable it will have to use other means to | |
653 | * fill in holes in what would be a sparse file. | |
654 | */ | |
655 | int | |
656 | pcfs_seek(vp, oldoff, newoff, cred) | |
657 | struct vnode *vp; | |
658 | off_t oldoff; | |
659 | off_t newoff; | |
660 | struct ucred *cred; | |
661 | { | |
662 | int error = 0; | |
663 | off_t foff; | |
664 | daddr_t bn; | |
665 | u_long cluster; | |
666 | u_long lastcluster; | |
667 | struct buf *bp; | |
668 | struct denode *dep = VTODE(vp); | |
669 | struct pcfsmount *pmp = dep->de_pmp; | |
670 | ||
671 | #if defined(PCFSDEBUG) | |
672 | printf("pcfs_seek(vp %08x, oldoff %d, newoff %d, cred %08x)\n", | |
673 | vp, oldoff, newoff, cred); | |
674 | #endif /* defined(PCFSDEBUG) */ | |
675 | ||
676 | /* | |
677 | * Compute the offset of the first byte after the | |
678 | * last block in the file. | |
679 | * If seeking beyond the end of file then fill the | |
680 | * file with zeroed blocks up to the seek address. | |
681 | */ | |
682 | foff = (dep->de_FileSize + (pmp->pm_bpcluster-1)) & ~pmp->pm_crbomask; | |
683 | #if defined(PCFSDEBUG) | |
684 | printf("seek: newoff %d > foff %d\n", newoff, foff); | |
685 | #endif /* defined(PCFSDEBUG) */ | |
686 | if (newoff > foff) { | |
687 | /* | |
688 | * If this is the root directory and we are | |
689 | * attempting to seek beyond the end disallow | |
690 | * it. DOS filesystem root directories can | |
691 | * not grow. | |
692 | */ | |
693 | if (vp->v_flag & VROOT) | |
694 | return EINVAL; | |
695 | /* | |
696 | * If this is a directory and the caller is not | |
697 | * root, then do not let them seek beyond the end | |
698 | * of file. If we allowed this then users could | |
699 | * cause directories to grow. Is this really that | |
700 | * important? | |
701 | */ | |
702 | if (dep->de_Attributes & ATTR_DIRECTORY) { | |
703 | if (error = suser(cred, NULL)) { | |
704 | return error; | |
705 | } | |
706 | } | |
707 | /* | |
708 | * Allocate and chain together as many clusters as | |
709 | * are needed to get to newoff. | |
710 | */ | |
711 | while (foff < newoff) { | |
712 | if (error = extendfile(dep, &bp, 0)) | |
713 | return error; | |
714 | dep->de_flag |= DEUPD; | |
715 | bdwrite(bp); | |
716 | foff += pmp->pm_bpcluster; | |
717 | dep->de_FileSize += pmp->pm_bpcluster; | |
718 | } /* end while() */ | |
719 | dep->de_FileSize = newoff; | |
fde1aeb2 GW |
720 | return deupdat(dep, &time, |
721 | pmp->pm_mountp->mnt_flag & MNT_SYNCHRONOUS); | |
15637ed4 RG |
722 | } |
723 | return 0; | |
724 | } | |
725 | ||
726 | int | |
727 | pcfs_remove(ndp, p) | |
728 | struct nameidata *ndp; | |
729 | struct proc *p; | |
730 | { | |
731 | int error; | |
732 | struct denode *dep = VTODE(ndp->ni_vp); | |
733 | struct denode *ddep = VTODE(ndp->ni_dvp); | |
734 | ||
735 | error = removede(ndp); | |
736 | #if defined(PCFSDEBUG) | |
737 | printf("pcfs_remove(), dep %08x, v_usecount %d\n", dep, ndp->ni_vp->v_usecount); | |
738 | #endif /* defined(PCFSDEBUG) */ | |
739 | if (ddep == dep) | |
740 | vrele(DETOV(dep)); | |
741 | else | |
742 | deput(dep); /* causes pcfs_inactive() to be called via vrele() */ | |
743 | deput(ddep); | |
744 | return error; | |
745 | } | |
746 | ||
747 | /* | |
748 | * DOS filesystems don't know what links are. | |
749 | */ | |
750 | int | |
751 | pcfs_link(vp, ndp, p) | |
752 | struct vnode *vp; | |
753 | struct nameidata *ndp; | |
754 | struct proc *p; | |
755 | { | |
756 | return EINVAL; | |
757 | } | |
758 | ||
759 | /* | |
760 | * Renames on files require moving the denode to | |
761 | * a new hash queue since the denode's location is | |
762 | * used to compute which hash queue to put the file in. | |
763 | * Unless it is a rename in place. For example "mv a b". | |
764 | * | |
765 | * What follows is the basic algorithm: | |
766 | * | |
767 | * if (file move) { | |
768 | * if (dest file exists) { | |
769 | * remove dest file | |
770 | * } | |
771 | * if (dest and src in same directory) { | |
772 | * rewrite name in existing directory slot | |
773 | * } else { | |
774 | * write new entry in dest directory | |
775 | * update offset and dirclust in denode | |
776 | * move denode to new hash chain | |
777 | * clear old directory entry | |
778 | * } | |
779 | * } else { directory move | |
780 | * if (dest directory exists) { | |
781 | * if (dest is not empty) { | |
782 | * return ENOTEMPTY | |
783 | * } | |
784 | * remove dest directory | |
785 | * } | |
786 | * if (dest and src in same directory) { | |
787 | * rewrite name in existing entry | |
788 | * } else { | |
789 | * be sure dest is not a child of src directory | |
790 | * write entry in dest directory | |
791 | * update "." and ".." in moved directory | |
792 | * update offset and dirclust in denode | |
793 | * move denode to new hash chain | |
794 | * clear old directory entry for moved directory | |
795 | * } | |
796 | * } | |
797 | * | |
798 | * On entry: | |
799 | * source's parent directory is unlocked | |
800 | * source file or directory is unlocked | |
801 | * destination's parent directory is locked | |
802 | * destination file or directory is locked if it exists | |
803 | * | |
804 | * On exit: | |
805 | * all denodes should be released | |
806 | * Notes: | |
807 | * I'm not sure how the memory containing the pathnames | |
808 | * pointed at by the nameidata structures is freed, there | |
809 | * may be some memory bleeding for each rename done. | |
810 | */ | |
811 | int | |
812 | pcfs_rename(fndp, tndp, p) | |
813 | struct nameidata *fndp; | |
814 | struct nameidata *tndp; | |
815 | struct proc *p; | |
816 | { | |
817 | u_char toname[11]; | |
818 | int error; | |
819 | int newparent = 0; | |
820 | int sourceisadirectory = 0; | |
821 | u_long to_dirclust; | |
822 | u_long to_diroffset; | |
823 | u_long cn; | |
824 | daddr_t bn; | |
825 | struct denode *fddep; /* from file's parent directory */ | |
826 | struct denode *fdep; /* from file or directory */ | |
827 | struct denode *tddep; /* to file's parent directory */ | |
828 | struct denode *tdep; /* to file or directory */ | |
829 | struct pcfsmount *pmp; | |
830 | struct direntry *dotdotp; | |
831 | struct direntry *ep; | |
832 | struct buf *bp; | |
833 | ||
834 | #if defined(PCFSDEBUG) | |
835 | printf("pcfs_rename(fndp %08x, tndp %08x, p %08x\n", fndp, tndp, p); | |
836 | #endif /* defined(PCFSDEBUG) */ | |
837 | fddep = VTODE(fndp->ni_dvp); | |
838 | fdep = VTODE(fndp->ni_vp); | |
839 | tddep = VTODE(tndp->ni_dvp); | |
840 | tdep = tndp->ni_vp ? VTODE(tndp->ni_vp) : NULL; | |
841 | pmp = fddep->de_pmp; | |
842 | ||
843 | /* | |
844 | * Convert the filename in tdnp into a dos filename. | |
845 | * We copy this into the denode and directory entry | |
846 | * for the destination file/directory. | |
847 | */ | |
848 | unix2dosfn((u_char *)tndp->ni_ptr, toname, tndp->ni_namelen); | |
849 | ||
850 | /* | |
851 | * At this point this is the lock state of the denodes: | |
852 | * fddep referenced | |
853 | * fdep referenced | |
854 | * tddep locked | |
855 | * tdep locked if it exists | |
856 | */ | |
857 | ||
858 | /* | |
859 | * Be sure we are not renaming ".", "..", or an alias of ".". | |
860 | * This leads to a crippled directory tree. It's pretty tough | |
861 | * to do a "ls" or "pwd" with the "." directory entry missing, | |
862 | * and "cd .." doesn't work if the ".." entry is missing. | |
863 | */ | |
864 | if (fdep->de_Attributes & ATTR_DIRECTORY) { | |
865 | if ((fndp->ni_namelen == 1 && fndp->ni_ptr[0] == '.') || | |
866 | fddep == fdep || /* won't happen ? */ | |
867 | fndp->ni_isdotdot) { | |
868 | VOP_ABORTOP(tndp); | |
869 | vput(tndp->ni_dvp); | |
870 | if (tndp->ni_vp) | |
871 | vput(tndp->ni_vp); | |
872 | VOP_ABORTOP(fndp); | |
873 | vrele(fndp->ni_dvp); | |
874 | vrele(fndp->ni_vp); | |
875 | return EINVAL; | |
876 | } | |
877 | sourceisadirectory = 1; | |
878 | } | |
879 | ||
880 | /* | |
881 | * If we are renaming a directory, and the directory | |
882 | * is being moved to another directory, then we must | |
883 | * be sure the destination directory is not in the | |
884 | * subtree of the source directory. This could orphan | |
885 | * everything under the source directory. | |
886 | * doscheckpath() unlocks the destination's parent | |
887 | * directory so we must look it up again to relock it. | |
888 | */ | |
889 | if (fddep->de_StartCluster != tddep->de_StartCluster) | |
890 | newparent = 1; | |
891 | if (sourceisadirectory && newparent) { | |
892 | if (tdep) { | |
893 | deput(tdep); | |
894 | tdep = NULL; | |
895 | } | |
896 | /* doscheckpath() deput()'s tddep */ | |
fde1aeb2 | 897 | error = doscheckpath(fdep, tddep); |
15637ed4 RG |
898 | tddep = NULL; |
899 | if (error) { | |
900 | goto bad; | |
901 | } | |
902 | if ((tndp->ni_nameiop & SAVESTART) == 0) | |
903 | panic("pcfs_rename(): lost to startdir"); | |
904 | if (error = lookup(tndp, p)) { | |
905 | goto bad; | |
906 | } | |
907 | tddep = VTODE(tndp->ni_dvp); | |
908 | tdep = tndp->ni_vp ? VTODE(tndp->ni_vp) : NULL; | |
909 | } | |
910 | ||
911 | /* | |
912 | * If the destination exists, then be sure its type (file or dir) | |
913 | * matches that of the source. And, if it is a directory make | |
914 | * sure it is empty. Then delete the destination. | |
915 | */ | |
916 | if (tdep) { | |
917 | if (tdep->de_Attributes & ATTR_DIRECTORY) { | |
918 | if (!sourceisadirectory) { | |
919 | error = ENOTDIR; | |
920 | goto bad; | |
921 | } | |
922 | if (!dosdirempty(tdep)) { | |
923 | error = ENOTEMPTY; | |
924 | goto bad; | |
925 | } | |
926 | } else { /* destination is file */ | |
927 | if (sourceisadirectory) { | |
928 | error = EISDIR; | |
929 | goto bad; | |
930 | } | |
931 | } | |
932 | to_dirclust = tdep->de_dirclust; | |
933 | to_diroffset = tdep->de_diroffset; | |
934 | if (error = removede(tndp)) { | |
935 | goto bad; | |
936 | } | |
937 | deput(tdep); | |
938 | tdep = NULL; | |
939 | ||
940 | /* | |
941 | * Remember where the slot was for createde(). | |
942 | */ | |
943 | tndp->ni_pcfs.pcfs_count = 1; | |
944 | tndp->ni_pcfs.pcfs_cluster = to_dirclust; | |
945 | tndp->ni_pcfs.pcfs_offset = to_diroffset; | |
946 | } | |
947 | ||
948 | /* | |
949 | * If the source and destination are in the same | |
950 | * directory then just read in the directory entry, | |
951 | * change the name in the directory entry and | |
952 | * write it back to disk. | |
953 | */ | |
954 | if (newparent == 0) { | |
955 | /* tddep and fddep point to the same denode here */ | |
956 | DELOCK(fdep); /* tddep is already locked */ | |
957 | if (error = readde(fdep, &bp, &ep)) { | |
958 | DEUNLOCK(fdep); | |
959 | goto bad; | |
960 | } | |
961 | bcopy(toname, ep->deName, 11); | |
962 | if (error = bwrite(bp)) { | |
963 | DEUNLOCK(fdep); | |
964 | goto bad; | |
965 | } | |
966 | bcopy(toname, fdep->de_Name, 11); /* update denode */ | |
967 | /* | |
968 | * fdep locked | |
969 | * fddep and tddep point to the same denode which is locked | |
970 | * tdep is unlocked and unreferenced | |
971 | */ | |
972 | } else { | |
4c45483e | 973 | u_long dirsize = 0; |
15637ed4 RG |
974 | /* |
975 | * If the source and destination are in different | |
976 | * directories, then mark the entry in the source | |
977 | * directory as deleted and write a new entry in the | |
978 | * destination directory. Then move the denode to | |
979 | * the correct hash chain for its new location in | |
980 | * the filesystem. And, if we moved a directory, | |
981 | * then update its .. entry to point to the new | |
982 | * parent directory. | |
983 | * If we moved a directory will also insure that | |
984 | * the directory entry on disk has a filesize of | |
985 | * zero. | |
986 | */ | |
987 | DELOCK(fdep); | |
988 | bcopy(toname, fdep->de_Name, 11); /* update denode */ | |
989 | if (fdep->de_Attributes & ATTR_DIRECTORY) { | |
990 | dirsize = fdep->de_FileSize; | |
991 | fdep->de_FileSize = 0; | |
992 | } | |
993 | error = createde(fdep, tndp, (struct denode **)0); | |
994 | if (fdep->de_Attributes & ATTR_DIRECTORY) { | |
995 | fdep->de_FileSize = dirsize; | |
996 | } | |
997 | if (error) { | |
998 | /* should put back filename */ | |
999 | DEUNLOCK(fdep); | |
1000 | goto bad; | |
1001 | } | |
1002 | DELOCK(fddep); | |
1003 | if (error = readde(fdep, &bp, &ep)) { /* read source de */ | |
1004 | DEUNLOCK(fdep); | |
1005 | DEUNLOCK(fddep); | |
1006 | goto bad; | |
1007 | } | |
1008 | ep->deName[0] = SLOT_DELETED; | |
1009 | if (error = bwrite(bp)) { | |
1010 | DEUNLOCK(fdep); | |
1011 | DEUNLOCK(fddep); | |
1012 | goto bad; | |
1013 | } | |
1014 | fdep->de_dirclust = tndp->ni_pcfs.pcfs_cluster; | |
1015 | fdep->de_diroffset = tndp->ni_pcfs.pcfs_offset; | |
1016 | reinsert(fdep); | |
1017 | DEUNLOCK(fddep); | |
1018 | } | |
1019 | /* fdep is still locked here */ | |
1020 | ||
1021 | /* | |
1022 | * If we moved a directory to a new parent directory, | |
1023 | * then we must fixup the ".." entry in the moved | |
1024 | * directory. | |
1025 | */ | |
1026 | if (sourceisadirectory && newparent) { | |
1027 | cn = fdep->de_StartCluster; | |
1028 | if (cn == PCFSROOT) { | |
1029 | /* this should never happen */ | |
1030 | panic("pcfs_rename(): updating .. in root directory?\n"); | |
1031 | } else { | |
1032 | bn = cntobn(pmp, cn); | |
1033 | } | |
1034 | error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster, | |
1035 | NOCRED, &bp); | |
1036 | if (error) { | |
1037 | /* should really panic here, fs is corrupt */ | |
1038 | DEUNLOCK(fdep); | |
1039 | goto bad; | |
1040 | } | |
1041 | dotdotp = (struct direntry *)bp->b_un.b_addr + 1; | |
1042 | dotdotp->deStartCluster = tddep->de_StartCluster; | |
1043 | error = bwrite(bp); | |
1044 | DEUNLOCK(fdep); | |
1045 | if (error) { | |
1046 | /* should really panic here, fs is corrupt */ | |
1047 | goto bad; | |
1048 | } | |
1049 | } else { | |
1050 | DEUNLOCK(fdep); | |
1051 | } | |
1052 | bad:; | |
1053 | vrele(DETOV(fdep)); | |
1054 | vrele(DETOV(fddep)); | |
1055 | if (tdep) | |
1056 | vput(DETOV(tdep)); | |
1057 | if (tddep) | |
1058 | vput(DETOV(tddep)); | |
1059 | return error; | |
1060 | } | |
1061 | ||
1062 | struct { | |
1063 | struct direntry dot; | |
1064 | struct direntry dotdot; | |
1065 | } dosdirtemplate = { | |
1066 | ". ", " ", /* the . entry */ | |
1067 | ATTR_DIRECTORY, /* file attribute */ | |
1068 | 0,0,0,0,0,0,0,0,0,0, /* resevered */ | |
1069 | 1234, 1234, /* time and date */ | |
1070 | 0, /* startcluster */ | |
1071 | 0, /* filesize */ | |
1072 | ".. ", " ", /* the .. entry */ | |
1073 | ATTR_DIRECTORY, /* file attribute */ | |
1074 | 0,0,0,0,0,0,0,0,0,0, /* resevered */ | |
1075 | 1234, 1234, /* time and date */ | |
1076 | 0, /* startcluster */ | |
1077 | 0, /* filesize */ | |
1078 | }; | |
1079 | ||
1080 | int | |
1081 | pcfs_mkdir(ndp, vap, p) | |
1082 | struct nameidata *ndp; | |
1083 | struct vattr *vap; | |
1084 | struct proc *p; | |
1085 | { | |
1086 | int bn; | |
1087 | int error; | |
1088 | u_long newcluster; | |
1089 | struct denode *pdep; | |
1090 | struct denode *ndep; | |
1091 | struct vnode *pvp; | |
1092 | struct direntry *denp; | |
1093 | struct denode ndirent; | |
1094 | struct pcfsmount *pmp; | |
1095 | struct buf *bp; | |
1096 | ||
1097 | pvp = ndp->ni_dvp; | |
1098 | pdep = VTODE(pvp); | |
1099 | ||
1100 | /* | |
1101 | * If this is the root directory and there is no space left | |
1102 | * we can't do anything. This is because the root directory | |
1103 | * can not change size. | |
1104 | */ | |
1105 | if (pdep->de_StartCluster == PCFSROOT && ndp->ni_pcfs.pcfs_count == 0) { | |
1106 | free(ndp->ni_pnbuf, M_NAMEI); | |
1107 | deput(pdep); | |
1108 | return ENOSPC; | |
1109 | } | |
1110 | ||
1111 | pmp = pdep->de_pmp; | |
1112 | ||
1113 | /* | |
1114 | * Allocate a cluster to hold the about to be created directory. | |
1115 | */ | |
1116 | if (error = clusteralloc(pmp, &newcluster, CLUST_EOFE)) { | |
1117 | free(ndp->ni_pnbuf, M_NAMEI); | |
1118 | deput(pdep); | |
1119 | return error; | |
1120 | } | |
1121 | ||
1122 | /* | |
1123 | * Now fill the cluster with the "." and ".." entries. | |
1124 | * And write the cluster to disk. This way it is there | |
1125 | * for the parent directory to be pointing at if there | |
1126 | * were a crash. | |
1127 | */ | |
1128 | bn = cntobn(pmp, newcluster); | |
1129 | bp = getblk(pmp->pm_devvp, bn, pmp->pm_bpcluster); /* always succeeds */ | |
1130 | bzero(bp->b_un.b_addr, pmp->pm_bpcluster); | |
1131 | bcopy(&dosdirtemplate, bp->b_un.b_addr, sizeof dosdirtemplate); | |
1132 | denp = (struct direntry *)bp->b_un.b_addr; | |
1133 | denp->deStartCluster = newcluster; | |
1134 | unix2dostime(&time, (union dosdate *)&denp->deDate, | |
1135 | (union dostime *)&denp->deTime); | |
1136 | denp++; | |
1137 | denp->deStartCluster = pdep->de_StartCluster; | |
1138 | unix2dostime(&time, (union dosdate *)&denp->deDate, | |
1139 | (union dostime *)&denp->deTime); | |
1140 | if (error = bwrite(bp)) { | |
1141 | clusterfree(pmp, newcluster, NULL); | |
1142 | free(ndp->ni_pnbuf, M_NAMEI); | |
1143 | deput(pdep); | |
1144 | return error; | |
1145 | } | |
1146 | ||
1147 | /* | |
1148 | * Now build up a directory entry pointing to the newly | |
1149 | * allocated cluster. This will be written to an empty | |
1150 | * slot in the parent directory. | |
1151 | */ | |
1152 | ndep = &ndirent; | |
1153 | bzero(ndep, sizeof(*ndep)); | |
1154 | unix2dosfn((u_char *)ndp->ni_ptr, ndep->de_Name, ndp->ni_namelen); | |
1155 | unix2dostime(&time, (union dosdate *)&ndep->de_Date, | |
1156 | (union dostime *)&ndep->de_Time); | |
1157 | ndep->de_StartCluster = newcluster; | |
1158 | ndep->de_Attributes = ATTR_DIRECTORY; | |
1159 | ndep->de_pmp = pmp; /* createde() needs this */ | |
1160 | ||
1161 | error = createde(ndep, ndp, &ndep); | |
1162 | if (error) { | |
1163 | clusterfree(pmp, newcluster, NULL); | |
1164 | } else { | |
1165 | ndp->ni_vp = DETOV(ndep); | |
1166 | } | |
1167 | free(ndp->ni_pnbuf, M_NAMEI); | |
1168 | #if defined(PCFSDEBUG) | |
1169 | printf("pcfs_mkdir(): deput(%08x), vnode %08x\n", pdep, DETOV(pdep)); | |
1170 | #endif /* defined(PCFSDEBUG) */ | |
1171 | deput(pdep); | |
1172 | return error; | |
1173 | } | |
1174 | ||
1175 | int | |
1176 | pcfs_rmdir(ndp, p) | |
1177 | struct nameidata *ndp; | |
1178 | struct proc *p; | |
1179 | { | |
1180 | struct denode *ddep; | |
1181 | struct denode *dep; | |
1182 | int error = 0; | |
1183 | ||
1184 | ddep = VTODE(ndp->ni_dvp); /* parent dir of dir to delete */ | |
1185 | dep = VTODE(ndp->ni_vp); /* directory to delete */ | |
1186 | ||
1187 | /* | |
1188 | * Don't let "rmdir ." go thru. | |
1189 | */ | |
1190 | if (ddep == dep) { | |
1191 | vrele(DETOV(dep)); | |
1192 | deput(dep); | |
1193 | return EINVAL; | |
1194 | } | |
1195 | ||
1196 | /* | |
1197 | * Be sure the directory being deleted is empty. | |
1198 | */ | |
1199 | if (dosdirempty(dep) == 0) { | |
1200 | error = ENOTEMPTY; | |
1201 | goto out; | |
1202 | } | |
1203 | ||
1204 | /* | |
1205 | * Delete the entry from the directory. For dos filesystems | |
1206 | * this gets rid of the directory entry on disk, the in memory | |
1207 | * copy still exists but the de_refcnt is <= 0. This prevents | |
1208 | * it from being found by deget(). When the deput() on dep is | |
1209 | * done we give up access and eventually pcfs_reclaim() will | |
1210 | * be called which will remove it from the denode cache. | |
1211 | */ | |
1212 | if (error = removede(ndp)) | |
1213 | goto out; | |
1214 | ||
1215 | /* | |
1216 | * This is where we decrement the link count in the parent | |
1217 | * directory. Since dos filesystems don't do this we just | |
1218 | * purge the name cache and let go of the parent directory | |
1219 | * denode. | |
1220 | */ | |
1221 | cache_purge(DETOV(ddep)); | |
1222 | deput(ddep); | |
1223 | ndp->ni_dvp = NULL; /* getting rid of parent dir pointer? */ | |
1224 | ||
1225 | /* | |
1226 | * Truncate the directory that is being deleted. | |
1227 | */ | |
1228 | error = detrunc(dep, (u_long)0, IO_SYNC); | |
1229 | cache_purge(DETOV(dep)); | |
1230 | ||
1231 | out:; | |
1232 | if (ndp->ni_dvp) | |
1233 | deput(ddep); | |
1234 | deput(dep); | |
1235 | return error; | |
1236 | } | |
1237 | ||
1238 | /* | |
1239 | * DOS filesystems don't know what symlinks are. | |
1240 | */ | |
1241 | int | |
1242 | pcfs_symlink(ndp, vap, target, p) | |
1243 | struct nameidata *ndp; | |
1244 | struct vattr *vap; | |
1245 | char *target; | |
1246 | struct proc *p; | |
1247 | { | |
1248 | return EINVAL; | |
1249 | } | |
1250 | ||
1251 | /* | |
1252 | * Dummy dirents to simulate the "." and ".." entries | |
1253 | * of the root directory in a dos filesystem. Dos doesn't | |
1254 | * provide these. | |
1255 | * Note that each entry must be the same size as a dos | |
1256 | * directory entry (32 bytes). | |
1257 | */ | |
1258 | struct dos_dirent { | |
1259 | u_long d_fileno; | |
1260 | u_short d_reclen; | |
1261 | u_short d_namlen; | |
1262 | u_char d_name[24]; | |
1263 | } rootdots[2] = { | |
1264 | { | |
1265 | 1, /* d_fileno */ | |
1266 | sizeof(struct direntry), /* d_reclen */ | |
1267 | 1, /* d_namlen */ | |
1268 | "." /* d_name */ | |
1269 | }, | |
1270 | { | |
1271 | 1, /* d_fileno */ | |
1272 | sizeof(struct direntry), /* d_reclen */ | |
1273 | 2, /* d_namlen */ | |
1274 | ".." /* d_name */ | |
1275 | } | |
1276 | }; | |
1277 | ||
1278 | int | |
1279 | pcfs_readdir(vp, uio, cred, eofflagp) | |
1280 | struct vnode *vp; | |
1281 | struct uio *uio; | |
1282 | struct ucred *cred; | |
1283 | int *eofflagp; | |
1284 | { | |
1285 | int error = 0; | |
1286 | int diff; | |
1287 | char pushout; | |
1288 | long n; | |
1289 | long on; | |
1290 | long lost; | |
1291 | long count; | |
1292 | u_long cn; | |
1293 | u_long fileno; | |
1294 | long bias = 0; | |
1295 | daddr_t bn; | |
1296 | daddr_t lbn; | |
1297 | struct buf *bp; | |
1298 | struct denode *dep = VTODE(vp); | |
1299 | struct pcfsmount *pmp = dep->de_pmp; | |
1300 | struct direntry *dentp; | |
1301 | struct dirent *prev; | |
1302 | struct dirent *crnt; | |
1303 | u_char dirbuf[512]; /* holds converted dos directories */ | |
1304 | int i=0; | |
1305 | ||
1306 | #if defined(PCFSDEBUG) | |
1307 | printf("pcfs_readdir(): vp %08x, uio %08x, cred %08x, eofflagp %08x\n", | |
1308 | vp, uio, cred, eofflagp); | |
1309 | #endif /* defined(PCFSDEBUG) */ | |
1310 | ||
1311 | /* | |
1312 | * pcfs_readdir() won't operate properly on regular files | |
1313 | * since it does i/o only with the the filesystem vnode, | |
1314 | * and hence can retrieve the wrong block from the buffer | |
1315 | * cache for a plain file. So, fail attempts to readdir() | |
1316 | * on a plain file. | |
1317 | */ | |
1318 | if ((dep->de_Attributes & ATTR_DIRECTORY) == 0) | |
1319 | return ENOTDIR; | |
1320 | ||
1321 | /* | |
1322 | * If the user buffer is smaller than the size of one dos | |
1323 | * directory entry or the file offset is not a multiple of | |
1324 | * the size of a directory entry, then we fail the read. | |
1325 | */ | |
1326 | count = uio->uio_resid & ~(sizeof(struct direntry)-1); | |
1327 | lost = uio->uio_resid - count; | |
1328 | if (count < sizeof(struct direntry) || | |
1329 | (uio->uio_offset & (sizeof(struct direntry)-1))) | |
1330 | return EINVAL; | |
1331 | uio->uio_resid = count; | |
1332 | uio->uio_iov->iov_len = count; | |
1333 | ||
1334 | /* | |
1335 | * If they are reading from the root directory then, | |
1336 | * we simulate the . and .. entries since these don't | |
1337 | * exist in the root directory. We also set the offset | |
1338 | * bias to make up for having to simulate these entries. | |
1339 | * By this I mean that at file offset 64 we read the first entry | |
1340 | * in the root directory that lives on disk. | |
1341 | */ | |
1342 | if (dep->de_StartCluster == PCFSROOT) { | |
1343 | /*printf("pcfs_readdir(): going after . or .. in root dir, offset %d\n", | |
1344 | uio->uio_offset);*/ | |
1345 | bias = 2*sizeof(struct direntry); | |
1346 | if (uio->uio_offset < 2*sizeof(struct direntry)) { | |
1347 | error = uiomove((char *)rootdots + uio->uio_offset, | |
1348 | sizeof rootdots - uio->uio_offset, uio); | |
1349 | if (error) | |
1350 | goto out; | |
1351 | } | |
1352 | } | |
1353 | do { | |
1354 | lbn = (uio->uio_offset-bias) >> pmp->pm_cnshift; | |
1355 | on = (uio->uio_offset-bias) & pmp->pm_crbomask; | |
1356 | n = MIN((u_long)(pmp->pm_bpcluster - on), uio->uio_resid); | |
1357 | diff = dep->de_FileSize - (uio->uio_offset - bias); | |
1358 | if (diff <= 0) | |
1359 | return 0; | |
1360 | if (diff < n) | |
1361 | n = diff; | |
1362 | error = pcbmap(dep, lbn, &bn, &cn); | |
1363 | if (error) | |
1364 | break; | |
1365 | error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster, NOCRED, &bp); | |
1366 | n = MIN(n, pmp->pm_bpcluster - bp->b_resid); | |
1367 | if (error) { | |
1368 | brelse(bp); | |
1369 | return error; | |
1370 | } | |
1371 | ||
1372 | /* | |
1373 | * code to convert from dos directory entries to ufs directory entries | |
1374 | */ | |
1375 | pushout = 0; | |
1376 | dentp = (struct direntry *)(bp->b_un.b_addr + on); | |
1377 | prev = 0; | |
1378 | crnt = (struct dirent *)dirbuf; | |
1379 | while ((char *)dentp < bp->b_un.b_addr + on + n) { | |
1380 | /*printf("rd: dentp %08x prev %08x crnt %08x deName %02x attr %02x\n", | |
1381 | dentp, prev, crnt, dentp->deName[0], dentp->deAttributes);*/ | |
1382 | /* | |
1383 | * If we have an empty entry or a slot from a deleted | |
1384 | * file, or a volume label entry just concatenate its | |
1385 | * space onto the end of the previous entry or, | |
1386 | * manufacture an empty entry if there is no previous | |
1387 | * entry. | |
1388 | */ | |
1389 | if (dentp->deName[0] == SLOT_EMPTY || | |
1390 | dentp->deName[0] == SLOT_DELETED || | |
1391 | (dentp->deAttributes & ATTR_VOLUME)) { | |
1392 | if (prev) { | |
1393 | prev->d_reclen += sizeof(struct direntry); | |
1394 | } else { | |
1395 | prev = crnt; | |
1396 | prev->d_fileno = 0; | |
1397 | prev->d_reclen = sizeof(struct direntry); | |
1398 | prev->d_namlen = 0; | |
1399 | prev->d_name[0] = 0; | |
1400 | } | |
1401 | } else { | |
1402 | /* this computation of d_fileno must match | |
1403 | * the computation of va_fileid in pcfs_getattr */ | |
1404 | if (dentp->deAttributes & ATTR_DIRECTORY) { | |
1405 | /* if this is the root directory */ | |
1406 | if ((fileno = dentp->deStartCluster) == PCFSROOT) | |
1407 | fileno = 1; | |
1408 | } else { | |
1409 | /* if the file's dirent lives in root dir */ | |
1410 | if ((fileno = cn) == PCFSROOT) | |
1411 | fileno = 1; | |
1412 | fileno = (fileno << 16) | | |
1413 | ((dentp - (struct direntry *)bp->b_un.b_addr) & 0xffff); | |
1414 | } | |
1415 | crnt->d_fileno = fileno; | |
1416 | crnt->d_reclen = sizeof(struct direntry); | |
1417 | crnt->d_namlen = dos2unixfn(dentp->deName, | |
1418 | (u_char *)crnt->d_name); | |
1419 | /*printf("readdir: file %s, fileno %08x, attr %02x, start %08x\n", | |
1420 | crnt->d_name, crnt->d_fileno, dentp->deAttributes, dentp->deStartCluster);*/ | |
1421 | prev = crnt; | |
1422 | } | |
1423 | crnt = (struct dirent *)((char *)crnt + sizeof(struct direntry)); | |
1424 | pushout = 1; | |
1425 | ||
1426 | /* | |
1427 | * If our intermediate buffer is full then copy | |
1428 | * its contents to user space. I would just | |
1429 | * use the buffer the buf header points to but, | |
1430 | * I'm afraid that when we brelse() it someone else | |
1431 | * might find it in the cache and think its contents | |
1432 | * are valid. Maybe there is a way to invalidate | |
1433 | * the buffer before brelse()'ing it. | |
1434 | */ | |
1435 | if ((u_char *)crnt >= &dirbuf[sizeof dirbuf]) { | |
1436 | pushout = 0; | |
1437 | error = uiomove(dirbuf, sizeof(dirbuf), uio); | |
1438 | if (error) | |
1439 | break; | |
1440 | prev = 0; | |
1441 | crnt = (struct dirent *)dirbuf; | |
1442 | } | |
1443 | dentp++; | |
1444 | } | |
1445 | if (pushout) { | |
1446 | pushout = 0; | |
1447 | error = uiomove(dirbuf, (char *)crnt - (char *)dirbuf, | |
1448 | uio); | |
1449 | } | |
1450 | ||
15637ed4 RG |
1451 | brelse(bp); |
1452 | } while (error == 0 && uio->uio_resid > 0 && n != 0); | |
1453 | out:; | |
1454 | uio->uio_resid += lost; | |
1455 | ||
1456 | /* | |
1457 | * I don't know why we bother setting this eofflag, getdirentries() | |
1458 | * in vfs_syscalls.c doesn't bother to look at it when we return. | |
1459 | */ | |
1460 | if (dep->de_FileSize - uio->uio_offset - bias <= 0) | |
1461 | *eofflagp = 1; | |
1462 | else | |
1463 | *eofflagp = 0; | |
1464 | return error; | |
1465 | } | |
1466 | ||
1467 | /* | |
1468 | * DOS filesystems don't know what symlinks are. | |
1469 | */ | |
1470 | int | |
1471 | pcfs_readlink(vp, uio, cred) | |
1472 | struct vnode *vp; | |
1473 | struct uio *uio; | |
1474 | struct ucred *cred; | |
1475 | { | |
1476 | return EINVAL; | |
1477 | } | |
1478 | ||
1479 | int | |
1480 | pcfs_abortop(ndp) | |
1481 | struct nameidata *ndp; | |
1482 | { | |
1483 | if ((ndp->ni_nameiop & (HASBUF | SAVESTART)) == HASBUF) | |
1484 | FREE(ndp->ni_pnbuf, M_NAMEI); | |
1485 | return 0; | |
1486 | } | |
1487 | ||
1488 | int | |
1489 | pcfs_lock(vp) | |
1490 | struct vnode *vp; | |
1491 | { | |
1492 | struct denode *dep = VTODE(vp); | |
1493 | ||
1494 | DELOCK(dep); | |
1495 | return 0; | |
1496 | } | |
1497 | ||
1498 | int | |
1499 | pcfs_unlock(vp) | |
1500 | struct vnode *vp; | |
1501 | { | |
1502 | struct denode *dep = VTODE(vp); | |
1503 | ||
1504 | if (!(dep->de_flag & DELOCKED)) | |
1505 | panic("pcfs_unlock: denode not locked"); | |
1506 | DEUNLOCK(dep); | |
1507 | return 0; | |
1508 | } | |
1509 | ||
1510 | int | |
1511 | pcfs_islocked(vp) | |
1512 | struct vnode *vp; | |
1513 | { | |
1514 | return VTODE(vp)->de_flag & DELOCKED ? 1 : 0; | |
1515 | } | |
1516 | ||
1517 | /* | |
1518 | * vp - address of vnode file the file | |
1519 | * bn - which cluster we are interested in mapping to | |
1520 | * a filesystem block number. | |
1521 | * vpp - returns the vnode for the block special file | |
1522 | * holding the filesystem containing the file of interest | |
1523 | * bnp - address of where to return the filesystem | |
1524 | * relative block number | |
1525 | */ | |
1526 | int | |
1527 | pcfs_bmap(vp, bn, vpp, bnp) | |
1528 | struct vnode *vp; | |
1529 | daddr_t bn; | |
1530 | struct vnode **vpp; | |
1531 | daddr_t *bnp; | |
1532 | { | |
1533 | struct denode *dep = VTODE(vp); | |
1534 | struct pcfsmount *pmp = dep->de_pmp; | |
1535 | ||
1536 | if (vpp != NULL) | |
1537 | *vpp = dep->de_devvp; | |
1538 | if (bnp == NULL) | |
1539 | return 0; | |
29708654 | 1540 | return pcbmap(dep, bn, bnp, 0); |
15637ed4 RG |
1541 | } |
1542 | ||
1543 | int | |
1544 | pcfs_strategy(bp) | |
1545 | struct buf *bp; | |
1546 | { | |
1547 | struct denode *dep = VTODE(bp->b_vp); | |
1548 | struct pcfsmount *pmp = dep->de_pmp; | |
1549 | struct vnode *vp; | |
1550 | int error; | |
1551 | ||
1552 | if (bp->b_vp->v_type == VBLK || bp->b_vp->v_type == VCHR) | |
1553 | panic("pcfs_strategy: spec"); | |
1554 | /* | |
1555 | * If we don't already know the filesystem relative | |
1556 | * block number then get it using pcbmap(). If pcbmap() | |
1557 | * returns the block number as -1 then we've got a hole | |
1558 | * in the file. DOS filesystems don't allow files with | |
1559 | * holes, so we shouldn't ever see this. | |
1560 | */ | |
1561 | if (bp->b_blkno == bp->b_lblkno) { | |
1562 | if (error = pcbmap(dep, bp->b_lblkno, &bp->b_blkno, 0)) | |
1563 | return error; | |
1564 | if ((long)bp->b_blkno == -1) | |
1565 | clrbuf(bp); | |
1566 | } | |
1567 | if ((long)bp->b_blkno == -1) { | |
1568 | biodone(bp); | |
1569 | return 0; | |
1570 | } | |
1571 | #ifdef DIAGNOSTIC | |
1572 | #endif /* defined(DIAGNOSTIC) */ | |
1573 | /* | |
1574 | * Read/write the block from/to the disk that contains the desired | |
1575 | * file block. | |
1576 | */ | |
1577 | vp = dep->de_devvp; | |
1578 | bp->b_dev = vp->v_rdev; | |
1579 | (*(vp->v_op->vop_strategy))(bp); | |
1580 | return 0; | |
1581 | } | |
1582 | ||
4c45483e | 1583 | void |
15637ed4 RG |
1584 | pcfs_print(vp) |
1585 | struct vnode *vp; | |
1586 | { | |
1587 | struct denode *dep = VTODE(vp); | |
1588 | ||
1589 | printf("tag VT_PCFS, startcluster %d, dircluster %d, diroffset %d ", | |
1590 | dep->de_StartCluster, dep->de_dirclust, dep->de_diroffset); | |
1591 | printf(" dev %d, %d, %s\n", | |
1592 | major(dep->de_dev), minor(dep->de_dev), | |
1593 | dep->de_flag & DELOCKED ? "(LOCKED)" : ""); | |
1594 | if (dep->de_spare0) { | |
1595 | printf(" owner pid %d", dep->de_spare0); | |
1596 | if (dep->de_spare1) | |
1597 | printf(" waiting pid %d", dep->de_spare1); | |
1598 | printf("\n"); | |
1599 | } | |
1600 | } | |
1601 | ||
1602 | int | |
1603 | pcfs_advlock(vp, id, op, fl, flags) | |
1604 | struct vnode *vp; | |
1605 | caddr_t id; | |
1606 | int op; | |
1607 | struct flock *fl; | |
1608 | int flags; | |
1609 | { | |
1610 | return EINVAL; /* we don't do locking yet */ | |
1611 | } | |
1612 | ||
1613 | struct vnodeops pcfs_vnodeops = { | |
1614 | pcfs_lookup, | |
1615 | pcfs_create, | |
1616 | pcfs_mknod, | |
1617 | pcfs_open, | |
1618 | pcfs_close, | |
1619 | pcfs_access, | |
1620 | pcfs_getattr, | |
1621 | pcfs_setattr, | |
1622 | pcfs_read, | |
1623 | pcfs_write, | |
1624 | pcfs_ioctl, | |
1625 | pcfs_select, | |
1626 | pcfs_mmap, | |
1627 | pcfs_fsync, | |
1628 | pcfs_seek, | |
1629 | pcfs_remove, | |
1630 | pcfs_link, | |
1631 | pcfs_rename, | |
1632 | pcfs_mkdir, | |
1633 | pcfs_rmdir, | |
1634 | pcfs_symlink, | |
1635 | pcfs_readdir, | |
1636 | pcfs_readlink, | |
1637 | pcfs_abortop, | |
1638 | pcfs_inactive, | |
1639 | pcfs_reclaim, | |
1640 | pcfs_lock, | |
1641 | pcfs_unlock, | |
1642 | pcfs_bmap, | |
1643 | pcfs_strategy, | |
1644 | pcfs_print, | |
1645 | pcfs_islocked, | |
1646 | pcfs_advlock, | |
1647 | }; |