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 <stdio.h> 63 #include <string.h> 64 #include <time.h> 65 #include <unistd.h> 66 67 #include "ffs/buf.h" 68 #include <fs/msdosfs/bpb.h> 69 #include "msdos/direntry.h" 70 #include <fs/msdosfs/denode.h> 71 #include <fs/msdosfs/fat.h> 72 #include <fs/msdosfs/msdosfsmount.h> 73 74 #include "makefs.h" 75 #include "msdos.h" 76 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 static void unix2fattime(const struct timespec *tsp, uint16_t *ddp, 97 uint16_t *dtp); 98 99 static void 100 msdosfs_times(struct denode *dep, const struct stat *st) 101 { 102 if (stampst.st_ino) 103 st = &stampst; 104 105 unix2fattime(&st->st_birthtim, &dep->de_CDate, &dep->de_CTime); 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 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(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 buf *bp; 417 struct direntry *dirp; 418 int error; 419 420 dep->de_flag &= ~DE_MODIFIED; 421 error = 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 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 MSDOSFS_DPRINTF(("open %s: %s", 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 MSDOSFS_DPRINTF(("%s: mmap %s: %s", __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(pmp->pm_devvp, bn, blsize, 0, &bp)) != 0) { 494 MSDOSFS_DPRINTF(("bread %d\n", error)); 495 goto out; 496 } 497 cpsize = MIN((nsize - offs), blsize - on); 498 memcpy(bp->b_data + on, dat + offs, cpsize); 499 bwrite(bp); 500 offs += cpsize; 501 } 502 503 munmap(dat, nsize); 504 return 0; 505 out: 506 munmap(dat, nsize); 507 return error; 508 } 509 510 static const struct { 511 struct direntry dot; 512 struct direntry dotdot; 513 } dosdirtemplate = { 514 { ". ", /* the . entry */ 515 ATTR_DIRECTORY, /* file attribute */ 516 0, /* reserved */ 517 0, { 0, 0 }, { 0, 0 }, /* create time & date */ 518 { 0, 0 }, /* access date */ 519 { 0, 0 }, /* high bits of start cluster */ 520 { 210, 4 }, { 210, 4 }, /* modify time & date */ 521 { 0, 0 }, /* startcluster */ 522 { 0, 0, 0, 0 } /* filesize */ 523 }, 524 { ".. ", /* the .. entry */ 525 ATTR_DIRECTORY, /* file attribute */ 526 0, /* reserved */ 527 0, { 0, 0 }, { 0, 0 }, /* create time & date */ 528 { 0, 0 }, /* access date */ 529 { 0, 0 }, /* high bits of start cluster */ 530 { 210, 4 }, { 210, 4 }, /* modify time & date */ 531 { 0, 0 }, /* startcluster */ 532 { 0, 0, 0, 0 } /* filesize */ 533 } 534 }; 535 536 struct denode * 537 msdosfs_mkdire(const char *path, struct denode *pdep, fsnode *node) { 538 struct denode ndirent; 539 struct denode *dep; 540 struct componentname cn; 541 struct msdosfsmount *pmp = pdep->de_pmp; 542 int error; 543 u_long newcluster, pcl, bn; 544 struct direntry *denp; 545 struct buf *bp; 546 547 cn.cn_nameptr = node->name; 548 cn.cn_namelen = strlen(node->name); 549 /* 550 * If this is the root directory and there is no space left we 551 * can't do anything. This is because the root directory can not 552 * change size. 553 */ 554 if (pdep->de_StartCluster == MSDOSFSROOT 555 && pdep->de_fndoffset >= pdep->de_FileSize) { 556 error = ENOSPC; 557 goto bad2; 558 } 559 560 /* 561 * Allocate a cluster to hold the about to be created directory. 562 */ 563 error = clusteralloc(pmp, 0, 1, CLUST_EOFE, &newcluster, NULL); 564 if (error) 565 goto bad2; 566 567 memset(&ndirent, 0, sizeof(ndirent)); 568 ndirent.de_pmp = pmp; 569 ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE; 570 msdosfs_times(&ndirent, &node->inode->st); 571 572 /* 573 * Now fill the cluster with the "." and ".." entries. And write 574 * the cluster to disk. This way it is there for the parent 575 * directory to be pointing at if there were a crash. 576 */ 577 bn = cntobn(pmp, newcluster); 578 MSDOSFS_DPRINTF(("%s(newcluster %lu, bn=%lu)\n", 579 __func__, newcluster, bn)); 580 /* always succeeds */ 581 bp = getblk(pmp->pm_devvp, bn, pmp->pm_bpcluster, 0, 0, 0); 582 memset(bp->b_data, 0, pmp->pm_bpcluster); 583 memcpy(bp->b_data, &dosdirtemplate, sizeof dosdirtemplate); 584 denp = (struct direntry *)bp->b_data; 585 putushort(denp[0].deStartCluster, newcluster); 586 putushort(denp[0].deCDate, ndirent.de_CDate); 587 putushort(denp[0].deCTime, ndirent.de_CTime); 588 denp[0].deCHundredth = ndirent.de_CHun; 589 putushort(denp[0].deADate, ndirent.de_ADate); 590 putushort(denp[0].deMDate, ndirent.de_MDate); 591 putushort(denp[0].deMTime, ndirent.de_MTime); 592 pcl = pdep->de_StartCluster; 593 MSDOSFS_DPRINTF(("%s(pcl %lu, rootdirblk=%lu)\n", __func__, pcl, 594 pmp->pm_rootdirblk)); 595 if (FAT32(pmp) && pcl == pmp->pm_rootdirblk) 596 pcl = 0; 597 putushort(denp[1].deStartCluster, pcl); 598 putushort(denp[1].deCDate, ndirent.de_CDate); 599 putushort(denp[1].deCTime, ndirent.de_CTime); 600 denp[1].deCHundredth = ndirent.de_CHun; 601 putushort(denp[1].deADate, ndirent.de_ADate); 602 putushort(denp[1].deMDate, ndirent.de_MDate); 603 putushort(denp[1].deMTime, ndirent.de_MTime); 604 if (FAT32(pmp)) { 605 putushort(denp[0].deHighClust, newcluster >> 16); 606 putushort(denp[1].deHighClust, pdep->de_StartCluster >> 16); 607 } else { 608 putushort(denp[0].deHighClust, 0); 609 putushort(denp[1].deHighClust, 0); 610 } 611 612 if ((error = bwrite(bp)) != 0) 613 goto bad; 614 615 /* 616 * Now build up a directory entry pointing to the newly allocated 617 * cluster. This will be written to an empty slot in the parent 618 * directory. 619 */ 620 if ((error = uniqdosname(pdep, &cn, ndirent.de_Name)) != 0) 621 goto bad; 622 623 ndirent.de_Attributes = ATTR_DIRECTORY; 624 ndirent.de_StartCluster = newcluster; 625 ndirent.de_FileSize = 0; 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