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