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