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