1 /* $NetBSD: msdosfs_vnops.c,v 1.17 2016/01/30 09:59:27 mlelstv Exp $ */ 2 3 /*- 4 * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank. 5 * Copyright (C) 1994, 1995, 1997 TooLs GmbH. 6 * All rights reserved. 7 * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. All advertising materials mentioning features or use of this software 18 * must display the following acknowledgement: 19 * This product includes software developed by TooLs GmbH. 20 * 4. The name of TooLs GmbH may not be used to endorse or promote products 21 * derived from this software without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR 24 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 25 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 27 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 28 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 29 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 30 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 31 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 32 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 */ 34 /* 35 * Written by Paul Popelka (paulp@uts.amdahl.com) 36 * 37 * You can do anything you want with this software, just don't say you wrote 38 * it, and don't remove this notice. 39 * 40 * This software is provided "as is". 41 * 42 * The author supplies this software to be publicly redistributed on the 43 * understanding that the author is not responsible for the correct 44 * functioning of this software in any circumstances and is not liable for 45 * any damages caused by this software. 46 * 47 * October 1992 48 */ 49 50 #include <sys/param.h> 51 #include <sys/mman.h> 52 #include <fcntl.h> 53 #include <unistd.h> 54 55 #include <ffs/buf.h> 56 57 #include <fs/msdosfs/bpb.h> 58 #include <fs/msdosfs/direntry.h> 59 #include <fs/msdosfs/denode.h> 60 #include <fs/msdosfs/msdosfsmount.h> 61 #include <fs/msdosfs/fat.h> 62 63 #include "makefs.h" 64 #include "msdos.h" 65 66 #ifdef MSDOSFS_DEBUG 67 #define DPRINTF(a) printf a 68 #else 69 #define DPRINTF(a) 70 #endif 71 /* 72 * Some general notes: 73 * 74 * In the ufs filesystem the inodes, superblocks, and indirect blocks are 75 * read/written using the vnode for the filesystem. Blocks that represent 76 * the contents of a file are read/written using the vnode for the file 77 * (including directories when they are read/written as files). This 78 * presents problems for the dos filesystem because data that should be in 79 * an inode (if dos had them) resides in the directory itself. Since we 80 * must update directory entries without the benefit of having the vnode 81 * for the directory we must use the vnode for the filesystem. This means 82 * that when a directory is actually read/written (via read, write, or 83 * readdir, or seek) we must use the vnode for the filesystem instead of 84 * the vnode for the directory as would happen in ufs. This is to insure we 85 * retrieve the correct block from the buffer cache since the hash value is 86 * based upon the vnode address and the desired block number. 87 */ 88 89 static int msdosfs_wfile(const char *, struct denode *, fsnode *); 90 91 static void 92 msdosfs_times(struct msdosfsmount *pmp, struct denode *dep, 93 const struct stat *st) 94 { 95 struct timespec at = st->st_atimespec; 96 struct timespec mt = st->st_mtimespec; 97 unix2dostime(&at, pmp->pm_minuteswest, &dep->de_ADate, NULL, NULL); 98 unix2dostime(&mt, pmp->pm_minuteswest, &dep->de_MDate, &dep->de_MTime, NULL); 99 } 100 101 /* 102 * When we search a directory the blocks containing directory entries are 103 * read and examined. The directory entries contain information that would 104 * normally be in the inode of a unix filesystem. This means that some of 105 * a directory's contents may also be in memory resident denodes (sort of 106 * an inode). This can cause problems if we are searching while some other 107 * process is modifying a directory. To prevent one process from accessing 108 * incompletely modified directory information we depend upon being the 109 * sole owner of a directory block. bread/brelse provide this service. 110 * This being the case, when a process modifies a directory it must first 111 * acquire the disk block that contains the directory entry to be modified. 112 * Then update the disk block and the denode, and then write the disk block 113 * out to disk. This way disk blocks containing directory entries and in 114 * memory denode's will be in synch. 115 */ 116 static int 117 msdosfs_findslot(struct denode *dp, struct componentname *cnp) 118 { 119 daddr_t bn; 120 int error; 121 int slotcount; 122 int slotoffset = 0; 123 int frcn; 124 u_long cluster; 125 int blkoff; 126 u_int diroff; 127 int blsize; 128 struct msdosfsmount *pmp; 129 struct buf *bp = 0; 130 struct direntry *dep; 131 u_char dosfilename[12]; 132 int wincnt = 1; 133 int chksum = -1, chksum_ok; 134 int olddos = 1; 135 136 pmp = dp->de_pmp; 137 138 switch (unix2dosfn(cnp->cn_nameptr, dosfilename, cnp->cn_namelen, 0)) { 139 case 0: 140 return (EINVAL); 141 case 1: 142 break; 143 case 2: 144 wincnt = winSlotCnt(cnp->cn_nameptr, cnp->cn_namelen) + 1; 145 break; 146 case 3: 147 olddos = 0; 148 wincnt = winSlotCnt(cnp->cn_nameptr, cnp->cn_namelen) + 1; 149 break; 150 } 151 152 if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME) 153 wincnt = 1; 154 155 /* 156 * Suppress search for slots unless creating 157 * file and at end of pathname, in which case 158 * we watch for a place to put the new file in 159 * case it doesn't already exist. 160 */ 161 slotcount = 0; 162 DPRINTF(("%s(): dos filename: %s\n", __func__, dosfilename)); 163 /* 164 * Search the directory pointed at by vdp for the name pointed at 165 * by cnp->cn_nameptr. 166 */ 167 /* 168 * The outer loop ranges over the clusters that make up the 169 * directory. Note that the root directory is different from all 170 * other directories. It has a fixed number of blocks that are not 171 * part of the pool of allocatable clusters. So, we treat it a 172 * little differently. The root directory starts at "cluster" 0. 173 */ 174 diroff = 0; 175 for (frcn = 0; diroff < dp->de_FileSize; frcn++) { 176 if ((error = pcbmap(dp, frcn, &bn, &cluster, &blsize)) != 0) { 177 if (error == E2BIG) 178 break; 179 return (error); 180 } 181 error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), blsize, 182 0, &bp); 183 if (error) { 184 return (error); 185 } 186 for (blkoff = 0; blkoff < blsize; 187 blkoff += sizeof(struct direntry), 188 diroff += sizeof(struct direntry)) { 189 dep = (struct direntry *)((char *)bp->b_data + blkoff); 190 /* 191 * If the slot is empty and we are still looking 192 * for an empty then remember this one. If the 193 * slot is not empty then check to see if it 194 * matches what we are looking for. If the slot 195 * has never been filled with anything, then the 196 * remainder of the directory has never been used, 197 * so there is no point in searching it. 198 */ 199 if (dep->deName[0] == SLOT_EMPTY || 200 dep->deName[0] == SLOT_DELETED) { 201 /* 202 * Drop memory of previous long matches 203 */ 204 chksum = -1; 205 206 if (slotcount < wincnt) { 207 slotcount++; 208 slotoffset = diroff; 209 } 210 if (dep->deName[0] == SLOT_EMPTY) { 211 brelse(bp, 0); 212 goto notfound; 213 } 214 } else { 215 /* 216 * If there wasn't enough space for our 217 * winentries, forget about the empty space 218 */ 219 if (slotcount < wincnt) 220 slotcount = 0; 221 222 /* 223 * Check for Win95 long filename entry 224 */ 225 if (dep->deAttributes == ATTR_WIN95) { 226 if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME) 227 continue; 228 229 chksum = winChkName(cnp->cn_nameptr, 230 cnp->cn_namelen, 231 (struct winentry *)dep, 232 chksum); 233 continue; 234 } 235 236 /* 237 * Ignore volume labels (anywhere, not just 238 * the root directory). 239 */ 240 if (dep->deAttributes & ATTR_VOLUME) { 241 chksum = -1; 242 continue; 243 } 244 245 /* 246 * Check for a checksum or name match 247 */ 248 chksum_ok = (chksum == winChksum(dep->deName)); 249 if (!chksum_ok 250 && (!olddos || memcmp(dosfilename, dep->deName, 11))) { 251 chksum = -1; 252 continue; 253 } 254 DPRINTF(("%s(): match blkoff %d, diroff %d\n", 255 __func__, blkoff, diroff)); 256 /* 257 * Remember where this directory 258 * entry came from for whoever did 259 * this lookup. 260 */ 261 dp->de_fndoffset = diroff; 262 dp->de_fndcnt = 0; 263 264 return EEXIST; 265 } 266 } /* for (blkoff = 0; .... */ 267 /* 268 * Release the buffer holding the directory cluster just 269 * searched. 270 */ 271 brelse(bp, 0); 272 } /* for (frcn = 0; ; frcn++) */ 273 274 notfound: 275 /* 276 * We hold no disk buffers at this point. 277 */ 278 279 /* 280 * If we get here we didn't find the entry we were looking for. But 281 * that's ok if we are creating or renaming and are at the end of 282 * the pathname and the directory hasn't been removed. 283 */ 284 DPRINTF(("%s(): refcnt %ld, slotcount %d, slotoffset %d\n", 285 __func__, dp->de_refcnt, slotcount, slotoffset)); 286 /* 287 * Fixup the slot description to point to the place where 288 * we might put the new DOS direntry (putting the Win95 289 * long name entries before that) 290 */ 291 if (!slotcount) { 292 slotcount = 1; 293 slotoffset = diroff; 294 } 295 if (wincnt > slotcount) { 296 slotoffset += sizeof(struct direntry) * (wincnt - slotcount); 297 } 298 299 /* 300 * Return an indication of where the new directory 301 * entry should be put. 302 */ 303 dp->de_fndoffset = slotoffset; 304 dp->de_fndcnt = wincnt - 1; 305 306 /* 307 * We return with the directory locked, so that 308 * the parameters we set up above will still be 309 * valid if we actually decide to do a direnter(). 310 * We return ni_vp == NULL to indicate that the entry 311 * does not currently exist; we leave a pointer to 312 * the (locked) directory inode in ndp->ni_dvp. 313 * 314 * NB - if the directory is unlocked, then this 315 * information cannot be used. 316 */ 317 return 0; 318 } 319 320 /* 321 * Create a regular file. On entry the directory to contain the file being 322 * created is locked. We must release before we return. 323 */ 324 struct denode * 325 msdosfs_mkfile(const char *path, struct denode *pdep, fsnode *node) 326 { 327 struct componentname cn; 328 struct denode ndirent; 329 struct denode *dep; 330 int error; 331 struct stat *st = &node->inode->st; 332 struct msdosfsmount *pmp = pdep->de_pmp; 333 334 cn.cn_nameptr = node->name; 335 cn.cn_namelen = strlen(node->name); 336 337 DPRINTF(("%s(name %s, mode 0%o size %zu)\n", __func__, node->name, 338 st->st_mode, (size_t)st->st_size)); 339 340 /* 341 * If this is the root directory and there is no space left we 342 * can't do anything. This is because the root directory can not 343 * change size. 344 */ 345 if (pdep->de_StartCluster == MSDOSFSROOT 346 && pdep->de_fndoffset >= pdep->de_FileSize) { 347 error = ENOSPC; 348 goto bad; 349 } 350 351 /* 352 * Create a directory entry for the file, then call createde() to 353 * have it installed. NOTE: DOS files are always executable. We 354 * use the absence of the owner write bit to make the file 355 * readonly. 356 */ 357 memset(&ndirent, 0, sizeof(ndirent)); 358 if ((error = uniqdosname(pdep, &cn, ndirent.de_Name)) != 0) 359 goto bad; 360 361 ndirent.de_Attributes = (st->st_mode & S_IWUSR) ? 362 ATTR_ARCHIVE : ATTR_ARCHIVE | ATTR_READONLY; 363 ndirent.de_StartCluster = 0; 364 ndirent.de_FileSize = 0; 365 ndirent.de_dev = pdep->de_dev; 366 ndirent.de_devvp = pdep->de_devvp; 367 ndirent.de_pmp = pdep->de_pmp; 368 ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE; 369 msdosfs_times(pmp, &ndirent, st); 370 if ((error = msdosfs_findslot(pdep, &cn)) != 0) 371 goto bad; 372 if ((error = createde(&ndirent, pdep, &dep, &cn)) != 0) 373 goto bad; 374 if ((error = msdosfs_wfile(path, dep, node)) != 0) 375 goto bad; 376 return dep; 377 378 bad: 379 errno = error; 380 return NULL; 381 } 382 static int 383 msdosfs_updatede(struct denode *dep) 384 { 385 struct buf *bp; 386 struct direntry *dirp; 387 int error; 388 389 dep->de_flag &= ~DE_MODIFIED; 390 error = readde(dep, &bp, &dirp); 391 if (error) 392 return error; 393 DE_EXTERNALIZE(dirp, dep); 394 error = bwrite(bp); 395 return error; 396 } 397 398 /* 399 * Write data to a file or directory. 400 */ 401 static int 402 msdosfs_wfile(const char *path, struct denode *dep, fsnode *node) 403 { 404 int error, fd; 405 size_t osize = dep->de_FileSize; 406 struct stat *st = &node->inode->st; 407 size_t nsize, offs; 408 struct msdosfsmount *pmp = dep->de_pmp; 409 struct buf *bp; 410 char *dat; 411 u_long cn = 0; 412 413 error = 0; /* XXX: gcc/vax */ 414 DPRINTF(("%s(diroff %lu, dirclust %lu, startcluster %lu)\n", __func__, 415 dep->de_diroffset, dep->de_dirclust, dep->de_StartCluster)); 416 if (st->st_size == 0) 417 return 0; 418 419 /* Don't bother to try to write files larger than the fs limit */ 420 if (st->st_size > MSDOSFS_FILESIZE_MAX) { 421 errno = EFBIG; 422 return -1; 423 } 424 425 nsize = st->st_size; 426 DPRINTF(("%s(nsize=%zu, osize=%zu)\n", __func__, nsize, osize)); 427 if (nsize > osize) { 428 if ((error = deextend(dep, nsize, NULL)) != 0) { 429 errno = error; 430 return -1; 431 } 432 if ((error = msdosfs_updatede(dep)) != 0) { 433 errno = error; 434 return -1; 435 } 436 } 437 438 if ((fd = open(path, O_RDONLY)) == -1) 439 err(1, "open %s", path); 440 441 if ((dat = mmap(0, nsize, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0)) 442 == MAP_FAILED) { 443 DPRINTF(("%s: mmap %s %s", __func__, node->name, 444 strerror(errno))); 445 close(fd); 446 goto out; 447 } 448 close(fd); 449 450 for (offs = 0; offs < nsize;) { 451 int blsize, cpsize; 452 daddr_t bn; 453 u_long on = offs & pmp->pm_crbomask; 454 #ifdef HACK 455 cn = dep->de_StartCluster; 456 if (cn == MSDOSFSROOT) { 457 DPRINTF(("%s: bad lbn %lu", __func__, cn)); 458 goto out; 459 } 460 bn = cntobn(pmp, cn); 461 blsize = pmp->pm_bpcluster; 462 #else 463 if ((error = pcbmap(dep, cn++, &bn, NULL, &blsize)) != 0) { 464 DPRINTF(("%s: pcbmap %lu", __func__, bn)); 465 goto out; 466 } 467 #endif 468 DPRINTF(("%s(cn=%lu, bn=%llu/%llu, blsize=%d)\n", __func__, 469 cn, (unsigned long long)bn, 470 (unsigned long long)de_bn2kb(pmp, bn), blsize)); 471 if ((error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), blsize, 472 0, &bp)) != 0) { 473 DPRINTF(("bread %d\n", error)); 474 goto out; 475 } 476 cpsize = MIN((nsize - offs), blsize - on); 477 memcpy((char *)bp->b_data + on, dat + offs, cpsize); 478 bwrite(bp); 479 offs += cpsize; 480 } 481 482 munmap(dat, nsize); 483 return 0; 484 out: 485 munmap(dat, nsize); 486 return error; 487 } 488 489 490 static const struct { 491 struct direntry dot; 492 struct direntry dotdot; 493 } dosdirtemplate = { 494 { ". ", " ", /* the . entry */ 495 ATTR_DIRECTORY, /* file attribute */ 496 0, /* reserved */ 497 0, { 0, 0 }, { 0, 0 }, /* create time & date */ 498 { 0, 0 }, /* access date */ 499 { 0, 0 }, /* high bits of start cluster */ 500 { 210, 4 }, { 210, 4 }, /* modify time & date */ 501 { 0, 0 }, /* startcluster */ 502 { 0, 0, 0, 0 } /* filesize */ 503 }, 504 { ".. ", " ", /* the .. entry */ 505 ATTR_DIRECTORY, /* file attribute */ 506 0, /* reserved */ 507 0, { 0, 0 }, { 0, 0 }, /* create time & date */ 508 { 0, 0 }, /* access date */ 509 { 0, 0 }, /* high bits of start cluster */ 510 { 210, 4 }, { 210, 4 }, /* modify time & date */ 511 { 0, 0 }, /* startcluster */ 512 { 0, 0, 0, 0 } /* filesize */ 513 } 514 }; 515 516 struct denode * 517 msdosfs_mkdire(const char *path, struct denode *pdep, fsnode *node) { 518 struct denode ndirent; 519 struct denode *dep; 520 struct componentname cn; 521 struct stat *st = &node->inode->st; 522 struct msdosfsmount *pmp = pdep->de_pmp; 523 int error; 524 u_long newcluster, pcl, bn; 525 daddr_t lbn; 526 struct direntry *denp; 527 struct buf *bp; 528 529 cn.cn_nameptr = node->name; 530 cn.cn_namelen = strlen(node->name); 531 /* 532 * If this is the root directory and there is no space left we 533 * can't do anything. This is because the root directory can not 534 * change size. 535 */ 536 if (pdep->de_StartCluster == MSDOSFSROOT 537 && pdep->de_fndoffset >= pdep->de_FileSize) { 538 error = ENOSPC; 539 goto bad2; 540 } 541 542 /* 543 * Allocate a cluster to hold the about to be created directory. 544 */ 545 error = clusteralloc(pmp, 0, 1, &newcluster, NULL); 546 if (error) 547 goto bad2; 548 549 memset(&ndirent, 0, sizeof(ndirent)); 550 ndirent.de_pmp = pmp; 551 ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE; 552 msdosfs_times(pmp, &ndirent, st); 553 554 /* 555 * Now fill the cluster with the "." and ".." entries. And write 556 * the cluster to disk. This way it is there for the parent 557 * directory to be pointing at if there were a crash. 558 */ 559 bn = cntobn(pmp, newcluster); 560 lbn = de_bn2kb(pmp, bn); 561 DPRINTF(("%s(newcluster %lu, bn=%lu, lbn=%lu)\n", __func__, newcluster, 562 bn, lbn)); 563 /* always succeeds */ 564 bp = getblk(pmp->pm_devvp, lbn, pmp->pm_bpcluster, 0, 0); 565 memset(bp->b_data, 0, pmp->pm_bpcluster); 566 memcpy(bp->b_data, &dosdirtemplate, sizeof dosdirtemplate); 567 denp = (struct direntry *)bp->b_data; 568 putushort(denp[0].deStartCluster, newcluster); 569 putushort(denp[0].deCDate, ndirent.de_CDate); 570 putushort(denp[0].deCTime, ndirent.de_CTime); 571 denp[0].deCTimeHundredth = ndirent.de_CHun; 572 putushort(denp[0].deADate, ndirent.de_ADate); 573 putushort(denp[0].deMDate, ndirent.de_MDate); 574 putushort(denp[0].deMTime, ndirent.de_MTime); 575 pcl = pdep->de_StartCluster; 576 DPRINTF(("%s(pcl %lu, rootdirblk=%lu)\n", __func__, pcl, 577 pmp->pm_rootdirblk)); 578 if (FAT32(pmp) && pcl == pmp->pm_rootdirblk) 579 pcl = 0; 580 putushort(denp[1].deStartCluster, pcl); 581 putushort(denp[1].deCDate, ndirent.de_CDate); 582 putushort(denp[1].deCTime, ndirent.de_CTime); 583 denp[1].deCTimeHundredth = ndirent.de_CHun; 584 putushort(denp[1].deADate, ndirent.de_ADate); 585 putushort(denp[1].deMDate, ndirent.de_MDate); 586 putushort(denp[1].deMTime, ndirent.de_MTime); 587 if (FAT32(pmp)) { 588 putushort(denp[0].deHighClust, newcluster >> 16); 589 putushort(denp[1].deHighClust, pdep->de_StartCluster >> 16); 590 } else { 591 putushort(denp[0].deHighClust, 0); 592 putushort(denp[1].deHighClust, 0); 593 } 594 595 if ((error = bwrite(bp)) != 0) 596 goto bad; 597 598 /* 599 * Now build up a directory entry pointing to the newly allocated 600 * cluster. This will be written to an empty slot in the parent 601 * directory. 602 */ 603 if ((error = uniqdosname(pdep, &cn, ndirent.de_Name)) != 0) 604 goto bad; 605 606 ndirent.de_Attributes = ATTR_DIRECTORY; 607 ndirent.de_StartCluster = newcluster; 608 ndirent.de_FileSize = 0; 609 ndirent.de_dev = pdep->de_dev; 610 ndirent.de_devvp = pdep->de_devvp; 611 ndirent.de_pmp = pdep->de_pmp; 612 if ((error = msdosfs_findslot(pdep, &cn)) != 0) 613 goto bad; 614 if ((error = createde(&ndirent, pdep, &dep, &cn)) != 0) 615 goto bad; 616 if ((error = msdosfs_updatede(dep)) != 0) 617 goto bad; 618 return dep; 619 620 bad: 621 clusterfree(pmp, newcluster, NULL); 622 bad2: 623 errno = error; 624 return NULL; 625 } 626