Print this page
| Split |
Close |
| Expand all |
| Collapse all |
--- old/usr/src/uts/common/fs/pcfs/pc_dir.c
+++ new/usr/src/uts/common/fs/pcfs/pc_dir.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
13 13 * When distributing Covered Code, include this CDDL HEADER in each
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 21
22 22 /*
23 23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 24 * Use is subject to license terms.
25 25 * Copyright 2016 Joyent, Inc.
26 26 */
27 27
28 28 #include <sys/param.h>
29 29 #include <sys/errno.h>
30 30 #include <sys/systm.h>
31 31 #include <sys/sysmacros.h>
32 32 #include <sys/buf.h>
33 33 #include <sys/vfs.h>
34 34 #include <sys/kmem.h>
35 35 #include <sys/vnode.h>
36 36 #include <sys/debug.h>
37 37 #include <sys/cmn_err.h>
38 38 #include <sys/sunddi.h>
39 39 #include <sys/fs/pc_label.h>
40 40 #include <sys/fs/pc_fs.h>
41 41 #include <sys/fs/pc_dir.h>
42 42 #include <sys/fs/pc_node.h>
43 43
44 44 static int pc_makedirentry(struct pcnode *dp, struct pcdir *direntries,
45 45 int ndirentries, struct vattr *vap, offset_t offset);
46 46 static int pc_dirempty(struct pcnode *);
47 47 static int pc_findentry(struct pcnode *, char *, struct pcslot *, offset_t *);
48 48 static int pc_parsename(char *, char *, char *);
49 49 static int pc_remove_long_fn(struct pcnode *pcp,
50 50 offset_t lfn_offset);
51 51 static int generate_short_name(struct pcnode *dp, char *namep,
52 52 struct pcdir *ep);
53 53 static struct pcdir *pc_name_to_pcdir(struct pcnode *dp, char *namep,
54 54 int ndirentries, int *errret);
55 55 static offset_t pc_find_free_space(struct pcnode *pcp, int ndirentries);
56 56 static int direntries_needed(struct pcnode *dp, char *namep);
57 57 static int pc_is_short_file_name(char *namep, int foldcase);
58 58 static int shortname_exists(struct pcnode *dp, char *fname, char *fext);
59 59 static int pc_dirfixdotdot(struct pcnode *cdp, struct pcnode *opdp,
60 60 struct pcnode *npdp);
61 61 /*
62 62 * Tunables
63 63 */
64 64 int enable_long_filenames = 1;
65 65
66 66 /*
67 67 * Lookup a name in a directory. Return a pointer to the pc_node
68 68 * which represents the entry.
69 69 */
70 70 int
71 71 pc_dirlook(
72 72 struct pcnode *dp, /* parent directory */
73 73 char *namep, /* name to lookup */
74 74 struct pcnode **pcpp) /* result */
75 75 {
76 76 struct vnode *vp;
77 77 struct pcslot slot;
78 78 int error;
79 79
80 80 PC_DPRINTF2(4, "pc_dirlook (dp %p name %s)\n", (void *)dp, namep);
81 81
82 82 if (!(dp->pc_entry.pcd_attr & PCA_DIR)) {
83 83 return (ENOTDIR);
84 84 }
85 85 vp = PCTOV(dp);
86 86 /*
87 87 * check now for changed disk, before any return(0)
88 88 */
89 89 if (error = pc_verify(VFSTOPCFS(vp->v_vfsp)))
90 90 return (error);
91 91
92 92 /*
93 93 * Null component name is synonym for directory being searched.
94 94 */
95 95 if (*namep == '\0') {
96 96 VN_HOLD(vp);
97 97 *pcpp = dp;
98 98 return (0);
99 99 }
100 100 /*
101 101 * The root directory does not have "." and ".." entries,
102 102 * so they are faked here.
103 103 */
104 104 if (vp->v_flag & VROOT) {
105 105 if (bcmp(namep, ".", 2) == 0 || bcmp(namep, "..", 3) == 0) {
106 106 VN_HOLD(vp);
107 107 *pcpp = dp;
108 108 return (0);
109 109 }
110 110 }
111 111 error = pc_findentry(dp, namep, &slot, NULL);
112 112 if (error == 0) {
113 113 *pcpp = pc_getnode(VFSTOPCFS(vp->v_vfsp),
114 114 slot.sl_blkno, slot.sl_offset, slot.sl_ep);
115 115 brelse(slot.sl_bp);
116 116 PC_DPRINTF1(4, "pc_dirlook: FOUND pcp=%p\n", (void *)*pcpp);
117 117 } else if (error == EINVAL) {
118 118 error = ENOENT;
119 119 }
120 120 return (error);
121 121 }
122 122
123 123 /*
124 124 * Enter a name in a directory.
125 125 */
126 126 int
127 127 pc_direnter(
128 128 struct pcnode *dp, /* directory to make entry in */
129 129 char *namep, /* name of entry */
130 130 struct vattr *vap, /* attributes of new entry */
131 131 struct pcnode **pcpp)
132 132 {
133 133 int error;
134 134 struct pcslot slot;
135 135 struct vnode *vp = PCTOV(dp);
136 136 struct pcfs *fsp = VFSTOPCFS(vp->v_vfsp);
137 137 offset_t offset;
138 138 daddr_t blkno;
139 139 int boff;
140 140 struct buf *bp = NULL;
141 141 struct pcdir *ep;
142 142
143 143 PC_DPRINTF4(4, "pc_dirent(dp %p, name %s, vap %p, pcpp %p\n",
144 144 (void *)dp, namep, (void *)vap, (void *)pcpp);
145 145
146 146 if (pcpp != NULL)
147 147 *pcpp = NULL;
148 148 /*
149 149 * Leading spaces are not allowed in DOS.
150 150 */
151 151 if (*namep == ' ')
152 152 return (EINVAL);
153 153 /*
154 154 * If name is "." or "..", just look it up.
155 155 */
156 156 if (PC_NAME_IS_DOT(namep) || PC_NAME_IS_DOTDOT(namep)) {
157 157 if (pcpp) {
158 158 error = pc_dirlook(dp, namep, pcpp);
159 159 if (error)
160 160 return (error);
161 161 }
162 162 return (EEXIST);
163 163 }
164 164 if (PCA_IS_HIDDEN(fsp, dp->pc_entry.pcd_attr)) {
165 165 return (EPERM);
166 166 }
167 167 /*
168 168 * Make sure directory has not been removed while fs was unlocked.
169 169 */
170 170 if (dp->pc_entry.pcd_filename[0] == PCD_ERASED) {
171 171 return (ENOENT);
172 172 }
173 173 error = pc_findentry(dp, namep, &slot, NULL);
174 174 if (error == 0) {
175 175 if (pcpp) {
176 176 *pcpp =
177 177 pc_getnode(fsp, slot.sl_blkno, slot.sl_offset,
178 178 slot.sl_ep);
179 179 error = EEXIST;
180 180 }
181 181 brelse(slot.sl_bp);
182 182 } else if (error == ENOENT) {
183 183 struct pcdir *direntries;
184 184 int ndirentries;
185 185
186 186 /*
187 187 * The entry does not exist. Check write permission in
188 188 * directory to see if entry can be created.
189 189 */
190 190 if (dp->pc_entry.pcd_attr & PCA_RDONLY) {
191 191 return (EPERM);
192 192 }
193 193 error = 0;
194 194 /*
195 195 * Make sure there is a slot.
196 196 */
197 197 if (slot.sl_status == SL_NONE)
198 198 panic("pc_direnter: no slot\n");
199 199 ndirentries = direntries_needed(dp, namep);
200 200 if (ndirentries == -1) {
201 201 return (EINVAL);
202 202 }
203 203
204 204 offset = pc_find_free_space(dp, ndirentries);
205 205 if (offset == -1) {
206 206 return (ENOSPC);
207 207 }
208 208
209 209 /*
210 210 * Make an entry from the supplied attributes.
211 211 */
212 212 direntries = pc_name_to_pcdir(dp, namep, ndirentries, &error);
213 213 if (direntries == NULL) {
214 214 return (error);
215 215 }
216 216 error = pc_makedirentry(dp, direntries, ndirentries, vap,
217 217 offset);
218 218 kmem_free(direntries, ndirentries * sizeof (struct pcdir));
219 219 if (error) {
220 220 return (error);
221 221 }
222 222 offset += (ndirentries - 1) * sizeof (struct pcdir);
223 223 boff = pc_blkoff(fsp, offset);
224 224 error = pc_blkatoff(dp, offset, &bp, &ep);
225 225 if (error) {
226 226 return (error);
227 227 }
228 228 blkno = pc_daddrdb(fsp, bp->b_blkno);
229 229 /*
230 230 * Get a pcnode for the new entry.
231 231 */
232 232 *pcpp = pc_getnode(fsp, blkno, boff, ep);
233 233 brelse(bp);
234 234 if (vap->va_type == VDIR)
235 235 (*pcpp)->pc_size = fsp->pcfs_clsize;
236 236
237 237 /*
238 238 * Write out the new entry in the parent directory.
239 239 */
240 240 error = pc_syncfat(fsp);
241 241 if (!error) {
242 242 error = pc_nodeupdate(*pcpp);
243 243 }
244 244 }
245 245 return (error);
246 246 }
247 247
248 248 /*
249 249 * Template for "." and ".." directory entries.
250 250 */
251 251 static struct {
252 252 struct pcdir t_dot; /* dot entry */
253 253 struct pcdir t_dotdot; /* dotdot entry */
254 254 } dirtemplate = {
255 255 {
256 256 ". ",
257 257 " ",
258 258 PCA_DIR
259 259 },
260 260 {
261 261 ".. ",
262 262 " ",
263 263 PCA_DIR
264 264 }
265 265 };
266 266
267 267 /*
268 268 * Convert an attributes structure into the short filename entry
269 269 * and write out the whole entry.
270 270 */
271 271 static int
272 272 pc_makedirentry(struct pcnode *dp, struct pcdir *direntries,
273 273 int ndirentries, struct vattr *vap, offset_t offset)
274 274 {
275 275 struct vnode *vp = PCTOV(dp);
276 276 struct pcfs *fsp = VFSTOPCFS(vp->v_vfsp);
277 277 int error;
278 278 struct pcdir *ep;
279 279 int boff;
280 280 int i;
281 281 struct buf *bp = NULL;
282 282 timestruc_t now;
283 283
284 284 if (vap != NULL && vap->va_mask & (AT_ATIME|AT_MTIME))
285 285 return (EOPNOTSUPP);
286 286
287 287 ep = &direntries[ndirentries - 1];
288 288 gethrestime(&now);
289 289 if (error = pc_tvtopct(&now, &ep->pcd_mtime))
290 290 return (error);
291 291
292 292 ep->pcd_crtime = ep->pcd_mtime;
293 293 ep->pcd_ladate = ep->pcd_mtime.pct_date;
294 294 ep->pcd_crtime_msec = 0;
295 295 ep->pcd_size = 0;
296 296 ep->pcd_attr = 0;
297 297 /*
298 298 * Fields we don't use.
299 299 */
300 300 ep->pcd_ntattr = 0;
301 301 if (!IS_FAT32(fsp))
302 302 ep->un.pcd_eattr = 0;
303 303
304 304 if (vap && ((vap->va_mode & 0222) == 0))
305 305 ep->pcd_attr |= PCA_RDONLY;
306 306 if (vap && (vap->va_type == VDIR)) {
307 307 pc_cluster32_t cn;
308 308
309 309 ep->pcd_attr |= PCA_DIR;
310 310 /*
311 311 * Make dot and dotdot entries for a new directory.
312 312 */
313 313 cn = pc_alloccluster(fsp, 0);
314 314 switch (cn) {
315 315 case PCF_FREECLUSTER:
316 316 return (ENOSPC);
317 317 case PCF_ERRORCLUSTER:
318 318 return (EIO);
319 319 }
320 320 bp = ngeteblk(fsp->pcfs_clsize);
321 321 bp->b_edev = fsp->pcfs_xdev;
322 322 bp->b_dev = cmpdev(bp->b_edev);
323 323 bp->b_blkno = pc_cldaddr(fsp, cn);
324 324 clrbuf(bp);
325 325 pc_setstartcluster(fsp, ep, cn);
326 326 pc_setstartcluster(fsp, &dirtemplate.t_dot, cn);
327 327 cn = pc_getstartcluster(fsp, &dp->pc_entry);
328 328 pc_setstartcluster(fsp, &dirtemplate.t_dotdot, cn);
329 329 dirtemplate.t_dot.pcd_mtime =
330 330 dirtemplate.t_dotdot.pcd_mtime = ep->pcd_mtime;
331 331 dirtemplate.t_dot.pcd_crtime =
332 332 dirtemplate.t_dotdot.pcd_crtime = ep->pcd_crtime;
333 333 dirtemplate.t_dot.pcd_ladate =
334 334 dirtemplate.t_dotdot.pcd_ladate = ep->pcd_ladate;
335 335 dirtemplate.t_dot.pcd_crtime_msec =
336 336 dirtemplate.t_dotdot.pcd_crtime_msec = 0;
337 337 bcopy(&dirtemplate,
338 338 bp->b_un.b_addr, sizeof (dirtemplate));
339 339 bwrite2(bp);
340 340 error = geterror(bp);
341 341 brelse(bp);
342 342 if (error) {
343 343 PC_DPRINTF0(1, "pc_makedirentry error");
344 344 pc_mark_irrecov(fsp);
345 345 return (EIO);
346 346 }
347 347 } else {
348 348 pc_setstartcluster(fsp, ep, 0);
349 349 }
350 350 bp = NULL;
351 351 for (i = 0, ep = NULL; i < ndirentries; i++, ep++) {
352 352 boff = pc_blkoff(fsp, offset);
353 353 if (boff == 0 || bp == NULL || boff >= bp->b_bcount) {
354 354 if (bp != NULL) {
355 355 /* always modified */
356 356 bwrite2(bp);
357 357 error = geterror(bp);
358 358 brelse(bp);
359 359 if (error)
360 360 return (error);
361 361 bp = NULL;
362 362 }
363 363 error = pc_blkatoff(dp, offset, &bp, &ep);
364 364 if (error)
365 365 return (error);
366 366 }
367 367
368 368 *ep = direntries[i];
369 369 offset += sizeof (struct pcdir);
370 370 }
371 371 if (bp != NULL) {
372 372 /* always modified */
373 373 bwrite2(bp);
374 374 error = geterror(bp);
375 375 brelse(bp);
376 376 if (error)
377 377 return (error);
378 378 }
379 379 return (0);
380 380 }
381 381
382 382 /*
383 383 * Remove a name from a directory.
384 384 */
385 385 int
386 386 pc_dirremove(
387 387 struct pcnode *dp,
388 388 char *namep,
389 389 struct vnode *cdir,
390 390 enum vtype type,
391 391 caller_context_t *ctp)
392 392 {
393 393 struct pcslot slot;
394 394 struct pcnode *pcp;
395 395 int error;
396 396 struct vnode *vp = PCTOV(dp);
397 397 struct pcfs *fsp = VFSTOPCFS(vp->v_vfsp);
398 398 offset_t lfn_offset = -1;
399 399
400 400 PC_DPRINTF2(4, "pc_dirremove (dp %p name %s)\n", (void *)dp, namep);
401 401 if ((dp->pc_entry.pcd_attr & PCA_RDONLY) ||
402 402 PCA_IS_HIDDEN(fsp, dp->pc_entry.pcd_attr)) {
403 403 return (EPERM);
404 404 }
405 405 error = pc_findentry(dp, namep, &slot, &lfn_offset);
406 406 if (error)
407 407 return (error);
408 408 if (slot.sl_flags == SL_DOT) {
409 409 error = EINVAL;
410 410 } else if (slot.sl_flags == SL_DOTDOT) {
411 411 error = ENOTEMPTY;
412 412 } else {
413 413 pcp =
414 414 pc_getnode(VFSTOPCFS(vp->v_vfsp),
415 415 slot.sl_blkno, slot.sl_offset, slot.sl_ep);
416 416 }
417 417 if (error) {
418 418 brelse(slot.sl_bp);
419 419 return (error);
420 420 }
421 421 if (type == VDIR) {
422 422 if (pcp->pc_entry.pcd_attr & PCA_DIR) {
423 423 if (PCTOV(pcp) == cdir)
424 424 error = EINVAL;
425 425 else if (!pc_dirempty(pcp))
426 426 error = ENOTEMPTY;
427 427 } else {
428 428 error = ENOTDIR;
429 429 }
430 430 } else {
431 431 if (pcp->pc_entry.pcd_attr & PCA_DIR)
432 432 error = EISDIR;
433 433 }
434 434 if (error == 0) {
435 435 /*
436 436 * Mark the in core node and on disk entry
437 437 * as removed. The slot may then be reused.
438 438 * The files clusters will be deallocated
439 439 * when the last reference goes away.
440 440 */
441 441 pcp->pc_eblkno = -1;
442 442 pcp->pc_entry.pcd_filename[0] = PCD_ERASED;
443 443 if (lfn_offset != -1) {
444 444 brelse(slot.sl_bp);
445 445 error = pc_remove_long_fn(dp, lfn_offset);
446 446 if (error) {
447 447 VN_RELE(PCTOV(pcp));
448 448 pc_mark_irrecov(VFSTOPCFS(vp->v_vfsp));
449 449 return (EIO);
450 450 }
451 451 } else {
452 452 slot.sl_ep->pcd_filename[0] = PCD_ERASED;
453 453 bwrite2(slot.sl_bp);
454 454 error = geterror(slot.sl_bp);
455 455 brelse(slot.sl_bp);
456 456 }
457 457 if (error) {
458 458 VN_RELE(PCTOV(pcp));
459 459 pc_mark_irrecov(VFSTOPCFS(vp->v_vfsp));
460 460 return (EIO);
461 461 } else if (type == VDIR) {
462 462 error = pc_truncate(pcp, 0L);
463 463 }
464 464
465 465 } else {
466 466 brelse(slot.sl_bp);
467 467 }
468 468
469 469 if (error == 0) {
470 470 if (type == VDIR) {
471 471 vnevent_rmdir(PCTOV(pcp), vp, namep, ctp);
472 472 } else {
473 473 vnevent_remove(PCTOV(pcp), vp, namep, ctp);
474 474 }
475 475 }
476 476
477 477 VN_RELE(PCTOV(pcp));
478 478
479 479 return (error);
480 480 }
481 481
482 482 /*
483 483 * Determine whether a directory is empty.
484 484 */
485 485 static int
486 486 pc_dirempty(struct pcnode *pcp)
487 487 {
488 488 struct buf *bp;
489 489 struct pcdir *ep;
490 490 offset_t offset;
491 491 int boff;
492 492 char c;
493 493 int error;
494 494 struct vnode *vp;
495 495
496 496 vp = PCTOV(pcp);
497 497 bp = NULL;
498 498
499 499 offset = 0;
500 500 for (;;) {
501 501
502 502 /*
503 503 * If offset is on a block boundary,
504 504 * read in the next directory block.
505 505 * Release previous if it exists.
506 506 */
507 507 boff = pc_blkoff(VFSTOPCFS(vp->v_vfsp), offset);
508 508 if (boff == 0 || bp == NULL || boff >= bp->b_bcount) {
509 509 if (bp != NULL)
510 510 brelse(bp);
511 511 if (error = pc_blkatoff(pcp, offset, &bp, &ep)) {
512 512 return (error);
513 513 }
514 514 }
515 515 if (PCDL_IS_LFN(ep)) {
516 516 error = pc_extract_long_fn(pcp, NULL, &ep, &offset,
517 517 &bp);
518 518 /*
519 519 * EINVAL means the lfn was invalid, so start with
520 520 * the next entry. Otherwise, an error occurred _or_
521 521 * the lfn is valid, either of which means the
522 522 * directory is not empty.
523 523 */
524 524 if (error == EINVAL)
525 525 continue;
526 526 else {
527 527 if (bp)
528 528 brelse(bp);
529 529 return (error);
530 530 }
531 531 }
532 532 c = ep->pcd_filename[0];
533 533 if (c == PCD_UNUSED)
534 534 break;
535 535 if ((c != '.') && (c != PCD_ERASED)) {
536 536 brelse(bp);
537 537 return (0);
538 538 }
539 539 if ((c == '.') && !PC_SHORTNAME_IS_DOT(ep->pcd_filename) &&
540 540 !PC_SHORTNAME_IS_DOTDOT(ep->pcd_filename)) {
541 541 brelse(bp);
542 542 return (0);
543 543 }
544 544 ep++;
545 545 offset += sizeof (struct pcdir);
546 546 }
547 547 if (bp != NULL)
548 548 brelse(bp);
549 549 return (1);
550 550 }
551 551
552 552 /*
553 553 * Rename a file.
554 554 */
555 555 int
556 556 pc_rename(
557 557 struct pcnode *dp, /* parent directory */
558 558 struct pcnode *tdp, /* target directory */
559 559 char *snm, /* source file name */
560 560 char *tnm, /* target file name */
561 561 caller_context_t *ctp)
562 562 {
563 563 struct pcnode *pcp; /* pcnode we are trying to rename */
564 564 struct pcnode *tpcp = NULL; /* pcnode that's in our way */
565 565 struct pcslot slot;
566 566 int error;
567 567 struct vnode *vp = PCTOV(dp);
568 568 struct vnode *svp = NULL;
569 569 struct pcfs *fsp = VFSTOPCFS(vp->v_vfsp);
570 570 int filecasechange = 0;
571 571 int oldisdir = 0;
572 572
573 573 PC_DPRINTF3(4, "pc_rename(0x%p, %s, %s)\n", (void *)dp, snm, tnm);
574 574 /*
575 575 * Leading spaces are not allowed in DOS.
576 576 */
577 577 if (*tnm == ' ')
578 578 return (EINVAL);
579 579 /*
580 580 * No dot or dotdot.
581 581 */
582 582 if (PC_NAME_IS_DOT(snm) || PC_NAME_IS_DOTDOT(snm) ||
583 583 PC_NAME_IS_DOT(tnm) || PC_NAME_IS_DOTDOT(tnm))
584 584 return (EINVAL);
585 585 /*
586 586 * Get the source node. We'll jump back to here if trying to
587 587 * move on top of an existing file, after deleting that file.
588 588 */
589 589 top:
590 590 error = pc_findentry(dp, snm, &slot, NULL);
591 591 if (error) {
592 592 return (error);
593 593 }
594 594 pcp = pc_getnode(VFSTOPCFS(vp->v_vfsp),
595 595 slot.sl_blkno, slot.sl_offset, slot.sl_ep);
596 596
597 597 brelse(slot.sl_bp);
598 598
599 599 if (pcp)
600 600 svp = PCTOV(pcp);
601 601
602 602 /*
603 603 * is the rename invalid, i.e. rename("a", "a/a")
604 604 */
605 605 if (pcp == tdp) {
606 606 if (svp)
607 607 VN_RELE(svp);
608 608 return (EINVAL);
609 609 }
610 610
611 611 /*
612 612 * Are we just changing the case of an existing name?
613 613 */
614 614 if ((dp->pc_scluster == tdp->pc_scluster) &&
615 615 (u8_strcmp(snm, tnm, 0, U8_STRCMP_CI_UPPER, U8_UNICODE_LATEST,
616 616 &error) == 0)) {
617 617 filecasechange = 1;
618 618 }
619 619
620 620 /*
621 621 * u8_strcmp detected an illegal character
622 622 */
623 623 if (error)
624 624 return (EINVAL);
625 625
626 626 oldisdir = pcp->pc_entry.pcd_attr & PCA_DIR;
627 627
628 628 /*
629 629 * see if the target exists
630 630 */
631 631 error = pc_findentry(tdp, tnm, &slot, NULL);
632 632 if (error == 0 && filecasechange == 0) {
633 633 /*
634 634 * Target exists. If it's a file, delete it. If it's
635 635 * a directory, bail.
636 636 */
637 637 int newisdir;
638 638
639 639 tpcp = pc_getnode(VFSTOPCFS(vp->v_vfsp),
640 640 slot.sl_blkno, slot.sl_offset, slot.sl_ep);
641 641
642 642 newisdir = tpcp->pc_entry.pcd_attr & PCA_DIR;
643 643
644 644 brelse(slot.sl_bp);
645 645
646 646 /*
647 647 * Error cases (from rename(2)):
648 648 * old is dir, new is dir: EEXIST
649 649 * old is dir, new is nondir: ENOTDIR
650 650 * old is nondir, new is dir: EISDIR
651 651 */
652 652 if (!newisdir) {
653 653 if (oldisdir) {
654 654 error = ENOTDIR;
655 655 } else {
656 656 /* nondir/nondir, remove target */
657 657 error = pc_dirremove(tdp, tnm,
658 658 (struct vnode *)NULL, VREG, ctp);
659 659 if (error == 0) {
660 660 vnevent_rename_dest(PCTOV(tpcp),
661 661 PCTOV(tdp), tnm, ctp);
662 662 VN_RELE(PCTOV(tpcp));
663 663 tpcp = NULL;
664 664 VN_RELE(PCTOV(pcp));
665 665 goto top;
666 666 }
667 667 }
668 668 } else if (oldisdir) {
669 669 /* dir/dir, remove target */
670 670 error = pc_dirremove(tdp, tnm,
671 671 (struct vnode *)NULL, VDIR, ctp);
672 672 if (error == 0) {
673 673 vnevent_rename_dest(PCTOV(tpcp), PCTOV(tdp),
674 674 tnm, ctp);
675 675 VN_RELE(PCTOV(tpcp));
676 676 tpcp = NULL;
677 677 VN_RELE(PCTOV(pcp));
678 678 goto top;
679 679 }
680 680 /* Follow rename(2)'s spec... */
681 681 if (error == ENOTEMPTY) {
682 682 error = EEXIST;
683 683 }
684 684 } else {
685 685 /* nondir/dir, bail */
686 686 error = EISDIR;
687 687 }
688 688 }
689 689
690 690 if ((error == 0) || (error == ENOENT)) {
691 691 offset_t lfn_offset = -1;
692 692 daddr_t blkno;
693 693 struct pcdir *direntries;
694 694 struct pcdir *ep;
695 695 int ndirentries;
696 696 pc_cluster16_t pct_lo;
697 697 pc_cluster16_t pct_hi;
698 698 offset_t offset;
699 699 int boff;
700 700 struct buf *bp = NULL;
701 701 uchar_t attr;
702 702 int size;
703 703 struct pctime mtime;
704 704 struct pctime crtime;
705 705 uchar_t ntattr;
706 706 ushort_t ladate;
707 707 ushort_t eattr;
708 708 uchar_t crtime_msec;
709 709
710 710 /*
711 711 * Rename the source.
712 712 */
713 713 /*
714 714 * Delete the old name, and create a new name.
715 715 */
716 716 if (filecasechange == 1 && error == 0)
717 717 brelse(slot.sl_bp);
718 718 ndirentries = direntries_needed(tdp, tnm);
719 719 if (ndirentries == -1) {
720 720 error = EINVAL;
721 721 goto done;
722 722 }
723 723 /*
724 724 * first see if we have enough space to create the new
725 725 * name before destroying the old one.
726 726 */
727 727 offset = pc_find_free_space(tdp, ndirentries);
728 728 if (offset == -1) {
729 729 error = ENOSPC;
730 730 goto done;
731 731 }
732 732
733 733 error = pc_findentry(dp, snm, &slot, &lfn_offset);
734 734 if (error) {
735 735 goto done;
736 736 }
737 737 pct_lo = slot.sl_ep->pcd_scluster_lo;
738 738 if (IS_FAT32(fsp))
739 739 pct_hi = slot.sl_ep->un.pcd_scluster_hi;
740 740 else
741 741 eattr = slot.sl_ep->un.pcd_eattr;
742 742 size = slot.sl_ep->pcd_size;
743 743 attr = slot.sl_ep->pcd_attr;
744 744 mtime = slot.sl_ep->pcd_mtime;
745 745 crtime = slot.sl_ep->pcd_crtime;
746 746 crtime_msec = slot.sl_ep->pcd_crtime_msec;
747 747 ntattr = slot.sl_ep->pcd_ntattr;
748 748 ladate = slot.sl_ep->pcd_ladate;
749 749
750 750 if (lfn_offset != -1) {
751 751 brelse(slot.sl_bp);
752 752 error = pc_remove_long_fn(dp, lfn_offset);
753 753 if (error) {
754 754 pc_mark_irrecov(VFSTOPCFS(vp->v_vfsp));
755 755 goto done;
756 756 }
757 757 } else {
758 758 slot.sl_ep->pcd_filename[0] =
759 759 pcp->pc_entry.pcd_filename[0] = PCD_ERASED;
760 760 bwrite2(slot.sl_bp);
761 761 error = geterror(slot.sl_bp);
762 762 brelse(slot.sl_bp);
763 763 }
764 764 if (error) {
765 765 pc_mark_irrecov(VFSTOPCFS(vp->v_vfsp));
766 766 error = EIO;
767 767 goto done;
768 768 }
769 769
770 770 /*
771 771 * Make an entry from the supplied attributes.
772 772 */
773 773 direntries = pc_name_to_pcdir(tdp, tnm, ndirentries, &error);
774 774 if (direntries == NULL) {
775 775 goto done;
776 776 }
777 777
778 778 error = pc_makedirentry(tdp, direntries, ndirentries, NULL,
779 779 offset);
780 780 kmem_free(direntries, ndirentries * sizeof (struct pcdir));
781 781 if (error) {
782 782 goto done;
783 783 }
784 784 /* advance to short name */
785 785 offset += (ndirentries - 1) * sizeof (struct pcdir);
786 786 boff = pc_blkoff(fsp, offset);
787 787 error = pc_blkatoff(tdp, offset, &bp, &ep);
788 788 if (error) {
789 789 goto done;
790 790 }
791 791 blkno = pc_daddrdb(fsp, bp->b_blkno);
792 792 ep->pcd_scluster_lo = pct_lo;
793 793 if (IS_FAT32(fsp))
794 794 ep->un.pcd_scluster_hi = pct_hi;
795 795 else
796 796 ep->un.pcd_eattr = eattr;
797 797 ep->pcd_size = size;
798 798 ep->pcd_attr = attr;
799 799 ep->pcd_mtime = mtime;
800 800 ep->pcd_crtime = crtime;
801 801 ep->pcd_crtime_msec = crtime_msec;
802 802 ep->pcd_ntattr = ntattr;
803 803 ep->pcd_ladate = ladate;
804 804 bwrite2(bp);
805 805 error = geterror(bp);
806 806 pcp->pc_eblkno = blkno;
807 807 pcp->pc_eoffset = boff;
808 808 pcp->pc_entry = *ep;
809 809 pcp->pc_flags |= PC_CHG;
810 810 brelse(bp);
811 811 if (error) {
812 812 pc_mark_irrecov(VFSTOPCFS(vp->v_vfsp));
813 813 error = EIO;
814 814 goto done;
815 815 }
816 816 /* No need to fix ".." if we're renaming within a dir */
817 817 if (oldisdir && dp != tdp) {
818 818 if ((error = pc_dirfixdotdot(pcp, dp, tdp)) != 0) {
819 819 goto done;
820 820 }
821 821 }
822 822 if ((error = pc_nodeupdate(pcp)) != 0) {
823 823 goto done;
824 824 }
825 825 }
826 826
827 827 if (error == 0) {
828 828 vnevent_rename_src(PCTOV(pcp), PCTOV(dp), snm, ctp);
829 829 vnevent_rename_dest_dir(PCTOV(tdp), PCTOV(pcp), tnm, ctp);
830 830 }
831 831
832 832 done:
833 833 if (tpcp != NULL)
834 834 VN_RELE(PCTOV(tpcp));
835 835 VN_RELE(PCTOV(pcp));
836 836
837 837 return (error);
838 838 }
839 839
840 840 /*
841 841 * Fix the ".." entry of the child directory so that it points to the
842 842 * new parent directory instead of the old one.
843 843 */
844 844 static int
845 845 pc_dirfixdotdot(struct pcnode *dp, /* child directory being moved */
846 846 struct pcnode *opdp, /* old parent directory */
847 847 struct pcnode *npdp) /* new parent directory */
848 848 {
849 849 pc_cluster32_t cn;
850 850 struct vnode *vp = PCTOV(dp);
851 851 struct pcfs *fsp = VFSTOPCFS(vp->v_vfsp);
852 852 int error = 0;
853 853 struct buf *bp = NULL;
854 854 struct pcdir *ep = NULL;
855 855 struct pcdir *tep = NULL;
856 856
857 857 /*
858 858 * set the new child's ".." directory entry starting cluster to
859 859 * point to the new parent's starting cluster
860 860 */
861 861 ASSERT(opdp != npdp);
862 862 error = pc_blkatoff(dp, (offset_t)0, &bp, &ep);
863 863 if (error) {
864 864 PC_DPRINTF0(1, "pc_dirfixdotdot: error in blkatoff\n");
865 865 return (error);
866 866 }
867 867 tep = ep;
868 868 ep++;
869 869 if (!PC_SHORTNAME_IS_DOT(tep->pcd_filename) &&
870 870 !PC_SHORTNAME_IS_DOTDOT(ep->pcd_filename)) {
871 871 PC_DPRINTF0(1, "pc_dirfixdotdot: mangled directory entry\n");
872 872 error = ENOTDIR;
873 873 return (error);
874 874 }
875 875 cn = pc_getstartcluster(fsp, &npdp->pc_entry);
876 876 pc_setstartcluster(fsp, ep, cn);
877 877
878 878 bwrite2(bp);
879 879 error = geterror(bp);
880 880 brelse(bp);
881 881 if (error) {
882 882 PC_DPRINTF0(1, "pc_dirfixdotdot: error in write\n");
883 883 pc_mark_irrecov(fsp);
884 884 return (EIO);
885 885 }
886 886 return (0);
887 887 }
888 888
889 889
890 890 /*
891 891 * Search a directory for an entry.
892 892 * The directory should be locked as this routine
893 893 * will sleep on I/O while searching.
894 894 */
895 895 static int
896 896 pc_findentry(
897 897 struct pcnode *dp, /* parent directory */
898 898 char *namep, /* name to lookup */
899 899 struct pcslot *slotp,
900 900 offset_t *lfn_offset)
901 901 {
902 902 offset_t offset;
903 903 struct pcdir *ep = NULL;
904 904 int boff;
905 905 int error;
906 906 struct vnode *vp;
907 907 struct pcfs *fsp;
908 908
909 909 vp = PCTOV(dp);
910 910 PC_DPRINTF2(6, "pc_findentry: looking for %s in dir 0x%p\n", namep,
911 911 (void *)dp);
912 912 slotp->sl_status = SL_NONE;
913 913 if (!(dp->pc_entry.pcd_attr & PCA_DIR)) {
914 914 return (ENOTDIR);
915 915 }
916 916 /*
917 917 * Verify that the dp is still valid on the disk
918 918 */
919 919 fsp = VFSTOPCFS(vp->v_vfsp);
920 920 error = pc_verify(fsp);
921 921 if (error)
922 922 return (error);
923 923
924 924 slotp->sl_bp = NULL;
925 925 offset = 0;
926 926 for (;;) {
927 927 /*
928 928 * If offset is on a block boundary,
929 929 * read in the next directory block.
930 930 * Release previous if it exists.
931 931 */
932 932 boff = pc_blkoff(fsp, offset);
933 933 if (boff == 0 || slotp->sl_bp == NULL ||
934 934 boff >= slotp->sl_bp->b_bcount) {
935 935 if (slotp->sl_bp != NULL) {
936 936 brelse(slotp->sl_bp);
937 937 slotp->sl_bp = NULL;
938 938 }
939 939 error = pc_blkatoff(dp, offset, &slotp->sl_bp, &ep);
940 940 if (error == ENOENT && slotp->sl_status == SL_NONE) {
941 941 slotp->sl_status = SL_EXTEND;
942 942 slotp->sl_offset = (int)offset;
943 943 }
944 944 if (error)
945 945 return (error);
946 946 }
947 947 if ((ep->pcd_filename[0] == PCD_UNUSED) ||
948 948 (ep->pcd_filename[0] == PCD_ERASED)) {
949 949 /*
950 950 * note empty slots, in case name is not found
951 951 */
952 952 if (slotp->sl_status == SL_NONE) {
953 953 slotp->sl_status = SL_FOUND;
954 954 slotp->sl_blkno = pc_daddrdb(fsp,
955 955 slotp->sl_bp->b_blkno);
956 956 slotp->sl_offset = boff;
957 957 }
958 958 /*
959 959 * If unused we've hit the end of the directory
960 960 */
961 961 if (ep->pcd_filename[0] == PCD_UNUSED)
962 962 break;
963 963 offset += sizeof (struct pcdir);
964 964 ep++;
965 965 continue;
966 966 }
967 967 if (PCDL_IS_LFN(ep)) {
968 968 offset_t t = offset;
969 969 if (pc_match_long_fn(dp, namep, &ep,
970 970 slotp, &offset) == 0) {
971 971 if (lfn_offset != NULL)
972 972 *lfn_offset = t;
973 973 return (0);
974 974 }
975 975 continue;
976 976 }
977 977 if (pc_match_short_fn(dp, namep, &ep, slotp, &offset) == 0)
978 978 return (0);
979 979 }
980 980 if (slotp->sl_bp != NULL) {
981 981 brelse(slotp->sl_bp);
982 982 slotp->sl_bp = NULL;
983 983 }
984 984 return (ENOENT);
985 985 }
986 986
987 987 /*
988 988 * Obtain the block at offset "offset" in file pcp.
989 989 */
990 990 int
991 991 pc_blkatoff(
992 992 struct pcnode *pcp,
993 993 offset_t offset,
994 994 struct buf **bpp,
995 995 struct pcdir **epp)
996 996 {
997 997 struct pcfs *fsp;
998 998 struct buf *bp;
999 999 int size;
1000 1000 int error;
1001 1001 daddr_t bn;
1002 1002
1003 1003 fsp = VFSTOPCFS(PCTOV(pcp)->v_vfsp);
1004 1004 size = pc_blksize(fsp, pcp, offset);
1005 1005 if (pc_blkoff(fsp, offset) >= size) {
1006 1006 PC_DPRINTF0(5, "pc_blkatoff: ENOENT\n");
1007 1007 return (ENOENT);
1008 1008 }
1009 1009 error = pc_bmap(pcp, pc_lblkno(fsp, offset), &bn, (uint_t *)0);
1010 1010 if (error)
1011 1011 return (error);
1012 1012
1013 1013 bp = bread(fsp->pcfs_xdev, bn, size);
1014 1014 if (bp->b_flags & B_ERROR) {
1015 1015 PC_DPRINTF0(1, "pc_blkatoff: error\n");
1016 1016 brelse(bp);
1017 1017 pc_mark_irrecov(fsp);
1018 1018 return (EIO);
1019 1019 }
1020 1020 if (epp) {
1021 1021 *epp =
1022 1022 (struct pcdir *)(bp->b_un.b_addr + pc_blkoff(fsp, offset));
1023 1023 }
1024 1024 *bpp = bp;
1025 1025 return (0);
1026 1026 }
1027 1027
1028 1028 /*
1029 1029 * Parse user filename into the pc form of "filename.extension".
1030 1030 * If names are too long for the format (and enable_long_filenames is set)
1031 1031 * it returns EINVAL (since either this name was read from the disk (so
1032 1032 * it must fit), _or_ we're trying to match a long file name (so we
1033 1033 * should fail). Tests for characters that are invalid in PCDOS and
1034 1034 * converts to upper case (unless foldcase is 0).
1035 1035 */
1036 1036 static int
1037 1037 pc_parsename(
1038 1038 char *namep,
1039 1039 char *fnamep,
1040 1040 char *fextp)
1041 1041 {
1042 1042 int n;
1043 1043 char c;
1044 1044
1045 1045 n = PCFNAMESIZE;
1046 1046 c = *namep++;
1047 1047 if (c == 0)
1048 1048 return (EINVAL);
1049 1049 if (c == '.') {
1050 1050 /*
1051 1051 * check for "." and "..".
1052 1052 */
1053 1053 *fnamep++ = c;
1054 1054 n--;
1055 1055 if (c = *namep++) {
1056 1056 if ((c != '.') || (c = *namep)) /* ".x" or "..x" */
1057 1057 return (EINVAL);
1058 1058 *fnamep++ = '.';
1059 1059 n--;
1060 1060 }
1061 1061 } else {
1062 1062 /*
1063 1063 * filename up to '.'
1064 1064 */
1065 1065 do {
1066 1066 if (n-- > 0) {
1067 1067 c = toupper(c);
1068 1068 if (!pc_validchar(c))
1069 1069 return (EINVAL);
1070 1070 *fnamep++ = c;
1071 1071 } else {
1072 1072 /* not short */
1073 1073 if (enable_long_filenames)
1074 1074 return (EINVAL);
1075 1075 }
1076 1076 } while ((c = *namep++) != '\0' && c != '.');
1077 1077 }
1078 1078 while (n-- > 0) { /* fill with blanks */
1079 1079 *fnamep++ = ' ';
1080 1080 }
1081 1081 /*
1082 1082 * remainder is extension
1083 1083 */
1084 1084 n = PCFEXTSIZE;
1085 1085 if (c == '.') {
1086 1086 while ((c = *namep++) != '\0' && n--) {
1087 1087 c = toupper(c);
1088 1088 if (!pc_validchar(c))
1089 1089 return (EINVAL);
1090 1090 *fextp++ = c;
1091 1091 }
1092 1092 if (enable_long_filenames && (c != '\0')) {
1093 1093 /* not short */
1094 1094 return (EINVAL);
1095 1095 }
1096 1096 }
1097 1097 while (n-- > 0) { /* fill with blanks */
1098 1098 *fextp++ = ' ';
1099 1099 }
1100 1100 return (0);
1101 1101 }
1102 1102
1103 1103 /*
1104 1104 * Match a long filename entry with 'namep'. Also return failure
1105 1105 * if the long filename isn't valid.
1106 1106 */
1107 1107 int
1108 1108 pc_match_long_fn(struct pcnode *pcp, char *namep, struct pcdir **epp,
1109 1109 struct pcslot *slotp, offset_t *offset)
1110 1110 {
1111 1111 struct pcdir *ep = (struct pcdir *)*epp;
1112 1112 struct vnode *vp = PCTOV(pcp);
1113 1113 struct pcfs *fsp = VFSTOPCFS(vp->v_vfsp);
1114 1114 int error = 0;
1115 1115 char lfn[PCMAXNAMLEN+1];
1116 1116
1117 1117 error = pc_extract_long_fn(pcp, lfn, epp, offset, &slotp->sl_bp);
1118 1118 if (error) {
1119 1119 if (error == EINVAL) {
1120 1120 return (ENOENT);
1121 1121 } else
1122 1122 return (error);
1123 1123 }
1124 1124 ep = *epp;
1125 1125 if ((u8_strcmp(lfn, namep, 0, U8_STRCMP_CI_UPPER,
1126 1126 U8_UNICODE_LATEST, &error) == 0) && (error == 0)) {
1127 1127 /* match */
1128 1128 slotp->sl_flags = 0;
1129 1129 slotp->sl_blkno = pc_daddrdb(fsp, slotp->sl_bp->b_blkno);
1130 1130 slotp->sl_offset = pc_blkoff(fsp, *offset);
1131 1131 slotp->sl_ep = ep;
1132 1132 return (0);
1133 1133 }
1134 1134 *offset += sizeof (struct pcdir);
1135 1135 ep++;
1136 1136 *epp = ep;
1137 1137 /* If u8_strcmp detected an error it's sufficient to rtn ENOENT */
1138 1138 return (ENOENT);
1139 1139 }
1140 1140
1141 1141 /*
1142 1142 * Match a short filename entry with namep.
1143 1143 */
1144 1144 int
1145 1145 pc_match_short_fn(struct pcnode *pcp, char *namep, struct pcdir **epp,
1146 1146 struct pcslot *slotp, offset_t *offset)
1147 1147 {
1148 1148 char fname[PCFNAMESIZE];
1149 1149 char fext[PCFEXTSIZE];
1150 1150 struct pcdir *ep = *epp;
1151 1151 int error;
1152 1152 struct vnode *vp = PCTOV(pcp);
1153 1153 struct pcfs *fsp = VFSTOPCFS(vp->v_vfsp);
1154 1154 int boff = pc_blkoff(fsp, *offset);
1155 1155
1156 1156 if (PCA_IS_HIDDEN(fsp, ep->pcd_attr)) {
1157 1157 *offset += sizeof (struct pcdir);
1158 1158 ep++;
1159 1159 *epp = ep;
1160 1160 return (ENOENT);
1161 1161 }
1162 1162
1163 1163 error = pc_parsename(namep, fname, fext);
1164 1164 if (error) {
1165 1165 *offset += sizeof (struct pcdir);
1166 1166 ep++;
1167 1167 *epp = ep;
1168 1168 return (error);
1169 1169 }
1170 1170
1171 1171 if ((bcmp(fname, ep->pcd_filename, PCFNAMESIZE) == 0) &&
1172 1172 (bcmp(fext, ep->pcd_ext, PCFEXTSIZE) == 0)) {
1173 1173 /*
1174 1174 * found the file
1175 1175 */
1176 1176 if (fname[0] == '.') {
1177 1177 if (fname[1] == '.')
1178 1178 slotp->sl_flags = SL_DOTDOT;
1179 1179 else
1180 1180 slotp->sl_flags = SL_DOT;
1181 1181 } else {
1182 1182 slotp->sl_flags = 0;
1183 1183 }
1184 1184 slotp->sl_blkno =
1185 1185 pc_daddrdb(fsp, slotp->sl_bp->b_blkno);
1186 1186 slotp->sl_offset = boff;
1187 1187 slotp->sl_ep = ep;
1188 1188 return (0);
1189 1189 }
1190 1190 *offset += sizeof (struct pcdir);
1191 1191 ep++;
1192 1192 *epp = ep;
1193 1193 return (ENOENT);
1194 1194 }
1195 1195
1196 1196 /*
1197 1197 * Remove a long filename entry starting at lfn_offset. It must be
1198 1198 * a valid entry or we wouldn't have gotten here. Also remove the
1199 1199 * short filename entry.
1200 1200 */
1201 1201 static int
1202 1202 pc_remove_long_fn(struct pcnode *pcp, offset_t lfn_offset)
1203 1203 {
1204 1204 struct vnode *vp = PCTOV(pcp);
1205 1205 struct pcfs *fsp = VFSTOPCFS(vp->v_vfsp);
1206 1206 int boff;
1207 1207 struct buf *bp = NULL;
1208 1208 struct pcdir *ep = NULL;
1209 1209 int error = 0;
1210 1210
1211 1211 /*
1212 1212 * if we're in here, we know that the lfn is in the proper format
1213 1213 * of <series-of-lfn-entries> followed by <sfn-entry>
1214 1214 */
1215 1215 for (;;) {
1216 1216 boff = pc_blkoff(fsp, lfn_offset);
1217 1217 if (boff == 0 || bp == NULL || boff >= bp->b_bcount) {
1218 1218 if (bp != NULL) {
1219 1219 bwrite2(bp);
1220 1220 error = geterror(bp);
1221 1221 brelse(bp);
1222 1222 if (error)
1223 1223 return (error);
1224 1224 bp = NULL;
1225 1225 }
1226 1226 error = pc_blkatoff(pcp, lfn_offset, &bp, &ep);
1227 1227 if (error)
1228 1228 return (error);
1229 1229 }
1230 1230 if (!PCDL_IS_LFN(ep)) {
1231 1231 /* done */
1232 1232 break;
1233 1233 }
1234 1234 /* zap it */
1235 1235 ep->pcd_filename[0] = PCD_ERASED;
1236 1236 ep->pcd_attr = 0;
1237 1237 lfn_offset += sizeof (struct pcdir);
1238 1238 ep++;
1239 1239 }
1240 1240 /* now we're on the short entry */
1241 1241
1242 1242 ep->pcd_filename[0] = PCD_ERASED;
1243 1243 ep->pcd_attr = 0;
1244 1244
1245 1245 if (bp != NULL) {
1246 1246 bwrite2(bp);
1247 1247 error = geterror(bp);
1248 1248 brelse(bp);
1249 1249 if (error)
1250 1250 return (error);
1251 1251 }
1252 1252 return (0);
1253 1253 }
1254 1254
1255 1255 /*
1256 1256 * Find (and allocate) space in the directory denoted by
1257 1257 * 'pcp'. for 'ndirentries' pcdir structures.
1258 1258 * Return the offset at which to start, or -1 for failure.
1259 1259 */
1260 1260 static offset_t
1261 1261 pc_find_free_space(struct pcnode *pcp, int ndirentries)
1262 1262 {
1263 1263 offset_t offset = 0;
1264 1264 offset_t spaceneeded = ndirentries * sizeof (struct pcdir);
1265 1265 offset_t spaceoffset;
1266 1266 offset_t spaceavail = 0;
1267 1267 int boff;
1268 1268 struct buf *bp = NULL;
1269 1269 struct vnode *vp = PCTOV(pcp);
1270 1270 struct pcfs *fsp = VFSTOPCFS(vp->v_vfsp);
1271 1271 struct pcdir *ep;
1272 1272 int error;
1273 1273
1274 1274 spaceoffset = offset;
1275 1275 while (spaceneeded > spaceavail) {
1276 1276 /*
1277 1277 * If offset is on a block boundary,
1278 1278 * read in the next directory block.
1279 1279 * Release previous if it exists.
1280 1280 */
1281 1281 boff = pc_blkoff(fsp, offset);
1282 1282 if (boff == 0 || bp == NULL || boff >= bp->b_bcount) {
1283 1283 if (bp != NULL) {
1284 1284 brelse(bp);
1285 1285 bp = NULL;
1286 1286 }
1287 1287 error = pc_blkatoff(pcp, offset, &bp, &ep);
1288 1288 if (error == ENOENT) {
1289 1289 daddr_t bn;
1290 1290
1291 1291 /* extend directory */
1292 1292 if (!IS_FAT32(fsp) && (vp->v_flag & VROOT))
1293 1293 return (-1);
1294 1294 while (spaceneeded > spaceavail) {
1295 1295 error = pc_balloc(pcp,
1296 1296 pc_lblkno(fsp, offset), 1, &bn);
1297 1297 if (error)
1298 1298 return (-1);
1299 1299 pcp->pc_size += fsp->pcfs_clsize;
1300 1300 spaceavail += fsp->pcfs_clsize;
1301 1301 offset += fsp->pcfs_clsize;
1302 1302 }
1303 1303 return (spaceoffset);
1304 1304 }
1305 1305 if (error)
1306 1306 return (-1);
1307 1307 }
1308 1308 if ((ep->pcd_filename[0] == PCD_UNUSED) ||
1309 1309 (ep->pcd_filename[0] == PCD_ERASED)) {
1310 1310 offset += sizeof (struct pcdir);
1311 1311 spaceavail += sizeof (struct pcdir);
1312 1312 ep++;
1313 1313 continue;
1314 1314 }
1315 1315 offset += sizeof (struct pcdir);
1316 1316 spaceavail = 0;
1317 1317 spaceoffset = offset;
1318 1318 ep++;
1319 1319 }
1320 1320 if (bp != NULL) {
1321 1321 brelse(bp);
1322 1322 }
1323 1323 return (spaceoffset);
1324 1324 }
1325 1325
1326 1326 /*
1327 1327 * Return how many long filename entries are needed.
1328 1328 * A maximum of PCLFNCHUNKSIZE characters per entry, plus one for a
1329 1329 * short filename.
1330 1330 */
1331 1331 static int
1332 1332 direntries_needed(struct pcnode *dp, char *namep)
1333 1333 {
1334 1334 struct pcdir ep;
1335 1335 uint16_t *w2_str;
1336 1336 size_t u8l, u16l;
1337 1337 int ret;
1338 1338
1339 1339 if (enable_long_filenames == 0) {
1340 1340 return (1);
1341 1341 }
1342 1342 if (pc_is_short_file_name(namep, 0)) {
1343 1343 (void) pc_parsename(namep, ep.pcd_filename, ep.pcd_ext);
1344 1344 if (!shortname_exists(dp, ep.pcd_filename, ep.pcd_ext)) {
1345 1345 return (1);
1346 1346 }
1347 1347 }
1348 1348 if (pc_valid_long_fn(namep, 1)) {
1349 1349 /*
1350 1350 * convert to UTF-16 or UNICODE for calculating the entries
1351 1351 * needed. Conversion will consume at the most 512 bytes
1352 1352 */
1353 1353 u16l = PCMAXNAMLEN + 1;
1354 1354 w2_str = (uint16_t *)kmem_zalloc(PCMAXNAM_UTF16, KM_SLEEP);
1355 1355 u8l = strlen(namep);
1356 1356 ret = uconv_u8tou16((const uchar_t *)namep, &u8l,
1357 1357 w2_str, &u16l, UCONV_OUT_LITTLE_ENDIAN);
1358 1358 kmem_free((caddr_t)w2_str, PCMAXNAM_UTF16);
1359 1359 if (ret == 0) {
1360 1360 ret = 1 + u16l / PCLFNCHUNKSIZE;
1361 1361 if (u16l % PCLFNCHUNKSIZE != 0)
1362 1362 ret++;
1363 1363 return (ret);
1364 1364 }
1365 1365 }
1366 1366 return (-1);
1367 1367 }
1368 1368
1369 1369 /*
1370 1370 * Allocate and return an array of pcdir structures for the passed-in
1371 1371 * name. ndirentries tells how many are required (including the short
1372 1372 * filename entry). Just allocate and fill them in properly here so they
1373 1373 * can be written out.
1374 1374 */
1375 1375 static struct pcdir *
1376 1376 pc_name_to_pcdir(struct pcnode *dp, char *namep, int ndirentries, int *errret)
1377 1377 {
1378 1378 struct pcdir *bpcdir;
1379 1379 struct pcdir *ep;
1380 1380 struct pcdir_lfn *lep;
1381 1381 int i;
1382 1382 uchar_t cksum;
1383 1383 int nchars;
1384 1384 int error = 0;
1385 1385 char *nameend;
1386 1386 uint16_t *w2_str;
1387 1387 size_t u8l, u16l;
1388 1388 int ret;
1389 1389
1390 1390 bpcdir = kmem_zalloc(ndirentries * sizeof (struct pcdir), KM_SLEEP);
1391 1391 ep = &bpcdir[ndirentries - 1];
1392 1392 if (ndirentries == 1) {
1393 1393 (void) pc_parsename(namep, ep->pcd_filename, ep->pcd_ext);
1394 1394 return (bpcdir);
1395 1395 }
1396 1396
1397 1397 /* Here we need to convert to UTF-16 or UNICODE for writing */
1398 1398
1399 1399 u16l = PCMAXNAMLEN + 1;
1400 1400 w2_str = (uint16_t *)kmem_zalloc(PCMAXNAM_UTF16, KM_SLEEP);
1401 1401 u8l = strlen(namep);
1402 1402 ret = uconv_u8tou16((const uchar_t *)namep, &u8l, w2_str, &u16l,
1403 1403 UCONV_OUT_LITTLE_ENDIAN);
1404 1404 if (ret != 0) {
1405 1405 kmem_free((caddr_t)w2_str, PCMAXNAM_UTF16);
1406 1406 *errret = ret;
1407 1407 return (NULL);
1408 1408 }
1409 1409 nameend = (char *)(w2_str + u16l);
1410 1410 u16l %= PCLFNCHUNKSIZE;
1411 1411 if (u16l != 0) {
1412 1412 nchars = u16l + 1;
1413 1413 nameend += 2;
1414 1414 } else {
1415 1415 nchars = PCLFNCHUNKSIZE;
1416 1416 }
1417 1417 nchars *= sizeof (uint16_t);
1418 1418
1419 1419 /* short file name */
1420 1420 error = generate_short_name(dp, namep, ep);
1421 1421 if (error) {
1422 1422 kmem_free(bpcdir, ndirentries * sizeof (struct pcdir));
1423 1423 *errret = error;
1424 1424 return (NULL);
1425 1425 }
1426 1426 cksum = pc_checksum_long_fn(ep->pcd_filename, ep->pcd_ext);
1427 1427 for (i = 0; i < (ndirentries - 1); i++) {
1428 1428 /* long file name */
1429 1429 nameend -= nchars;
1430 1430 lep = (struct pcdir_lfn *)&bpcdir[i];
1431 1431 set_long_fn_chunk(lep, nameend, nchars);
1432 1432 lep->pcdl_attr = PCDL_LFN_BITS;
1433 1433 lep->pcdl_checksum = cksum;
1434 1434 lep->pcdl_ordinal = (uchar_t)(ndirentries - i - 1);
1435 1435 nchars = PCLFNCHUNKSIZE * sizeof (uint16_t);
1436 1436 }
1437 1437 kmem_free((caddr_t)w2_str, PCMAXNAM_UTF16);
1438 1438 lep = (struct pcdir_lfn *)&bpcdir[0];
1439 1439 lep->pcdl_ordinal |= 0x40;
1440 1440 return (bpcdir);
1441 1441 }
1442 1442
1443 1443 static int
1444 1444 generate_short_name(struct pcnode *dp, char *namep, struct pcdir *inep)
1445 1445 {
1446 1446 int rev;
1447 1447 int nchars;
1448 1448 int i, j;
1449 1449 char *dot = NULL;
1450 1450 char fname[PCFNAMESIZE+1];
1451 1451 char fext[PCFEXTSIZE+1];
1452 1452 char scratch[8];
1453 1453 int error = 0;
1454 1454 struct pcslot slot;
1455 1455 char shortname[20];
1456 1456 int force_tilde = 0;
1457 1457
1458 1458 /*
1459 1459 * generate a unique short file name based on the long input name.
1460 1460 *
1461 1461 * Say, for "This is a very long filename.txt" generate
1462 1462 * "THISIS~1.TXT", or "THISIS~2.TXT" if that's already there.
1463 1463 * Skip invalid short name characters in the long name, plus
1464 1464 * a couple NT skips (space and reverse backslash).
1465 1465 *
1466 1466 * Unfortunately, since this name would be hidden by the normal
1467 1467 * lookup routine, we need to look for it ourselves. But luckily
1468 1468 * we don't need to look at the lfn entries themselves.
1469 1469 */
1470 1470 force_tilde = !pc_is_short_file_name(namep, 1);
1471 1471
1472 1472 /*
1473 1473 * Strip off leading invalid characters.
1474 1474 * We need this because names like '.login' are now ok, but the
1475 1475 * short name needs to be something like LOGIN~1.
1476 1476 */
1477 1477 for (; *namep != '\0'; namep++) {
1478 1478 if (*namep == ' ')
1479 1479 continue;
1480 1480 if (!pc_validchar(*namep) && !pc_validchar(toupper(*namep)))
1481 1481 continue;
1482 1482 break;
1483 1483 }
1484 1484 dot = strrchr(namep, '.');
1485 1485 if (dot != NULL) {
1486 1486 dot++;
1487 1487 for (j = 0, i = 0; j < PCFEXTSIZE; i++) {
1488 1488 if (dot[i] == '\0')
1489 1489 break;
1490 1490 /* skip valid, but not generally good characters */
1491 1491 if (dot[i] == ' ' || dot[i] == '\\')
1492 1492 continue;
1493 1493 if (pc_validchar(dot[i]))
1494 1494 fext[j++] = dot[i];
1495 1495 else if (pc_validchar(toupper(dot[i])))
1496 1496 fext[j++] = toupper(dot[i]);
1497 1497 }
1498 1498 for (i = j; i < PCFEXTSIZE; i++)
1499 1499 fext[i] = ' ';
1500 1500 dot--;
1501 1501 } else {
1502 1502 for (i = 0; i < PCFEXTSIZE; i++) {
1503 1503 fext[i] = ' ';
1504 1504 }
1505 1505 }
1506 1506 /*
1507 1507 * We know we're a long name, not a short name (or we wouldn't
1508 1508 * be here at all. But if uppercasing ourselves would be a short
1509 1509 * name, then we can possibly avoid the ~N format.
1510 1510 */
1511 1511 if (!force_tilde)
1512 1512 rev = 0;
1513 1513 else
1514 1514 rev = 1;
1515 1515 for (;;) {
1516 1516 bzero(fname, sizeof (fname));
1517 1517 nchars = PCFNAMESIZE;
1518 1518 if (rev) {
1519 1519 nchars--; /* ~ */
1520 1520 i = rev;
1521 1521 do {
1522 1522 nchars--;
1523 1523 i /= 10;
1524 1524 } while (i);
1525 1525 if (nchars <= 0) {
1526 1526 return (ENOSPC);
1527 1527 }
1528 1528 }
1529 1529 for (j = 0, i = 0; j < nchars; i++) {
1530 1530 if ((&namep[i] == dot) || (namep[i] == '\0'))
1531 1531 break;
1532 1532 /* skip valid, but not generally good characters */
1533 1533 if (namep[i] == ' ' || namep[i] == '\\')
1534 1534 continue;
1535 1535 if (pc_validchar(namep[i]))
1536 1536 fname[j++] = namep[i];
1537 1537 else if (pc_validchar(toupper(namep[i])))
1538 1538 fname[j++] = toupper(namep[i]);
1539 1539 }
1540 1540 if (rev) {
1541 1541 (void) sprintf(scratch, "~%d", rev);
1542 1542 (void) strcat(fname, scratch);
1543 1543 }
1544 1544 for (i = strlen(fname); i < PCFNAMESIZE; i++)
1545 1545 fname[i] = ' ';
1546 1546 /* now see if it exists */
1547 1547 (void) pc_fname_ext_to_name(shortname, fname, fext, 0);
1548 1548 error = pc_findentry(dp, shortname, &slot, NULL);
1549 1549 if (error == 0) {
1550 1550 /* found it */
1551 1551 brelse(slot.sl_bp);
1552 1552 rev++;
1553 1553 continue;
1554 1554 }
1555 1555 if (!shortname_exists(dp, fname, fext))
1556 1556 break;
1557 1557 rev++;
1558 1558 }
1559 1559 (void) strncpy(inep->pcd_filename, fname, PCFNAMESIZE);
1560 1560 (void) strncpy(inep->pcd_ext, fext, PCFEXTSIZE);
1561 1561 return (0);
1562 1562 }
1563 1563
1564 1564 /*
1565 1565 * Returns 1 if the passed-in filename is a short name, 0 if not.
1566 1566 */
1567 1567 static int
1568 1568 pc_is_short_file_name(char *namep, int foldcase)
1569 1569 {
1570 1570 int i;
1571 1571 char c;
1572 1572
1573 1573 for (i = 0; i < PCFNAMESIZE; i++, namep++) {
1574 1574 if (*namep == '\0')
1575 1575 return (1);
1576 1576 if (*namep == '.')
1577 1577 break;
1578 1578 if (foldcase)
1579 1579 c = toupper(*namep);
1580 1580 else
1581 1581 c = *namep;
1582 1582 if (!pc_validchar(c))
1583 1583 return (0);
1584 1584 }
1585 1585 if (*namep == '\0')
1586 1586 return (1);
1587 1587 if (*namep != '.')
1588 1588 return (0);
1589 1589 namep++;
1590 1590 for (i = 0; i < PCFEXTSIZE; i++, namep++) {
1591 1591 if (*namep == '\0')
1592 1592 return (1);
1593 1593 if (foldcase)
1594 1594 c = toupper(*namep);
1595 1595 else
1596 1596 c = *namep;
1597 1597 if (!pc_validchar(c))
1598 1598 return (0);
1599 1599 }
1600 1600 /* we should be done. If not... */
1601 1601 if (*namep == '\0')
1602 1602 return (1);
1603 1603 return (0);
1604 1604
1605 1605 }
1606 1606
1607 1607 /*
1608 1608 * We call this when we want to see if a short filename already exists
1609 1609 * in the filesystem as part of a long filename. When creating a short
1610 1610 * name (FILENAME.TXT from the user, or when generating one for a long
1611 1611 * filename), we cannot allow one that is part of a long filename.
1612 1612 * pc_findentry will find all the names that are visible (long or short),
1613 1613 * but will not crack any long filename entries.
1614 1614 */
1615 1615 static int
1616 1616 shortname_exists(struct pcnode *dp, char *fname, char *fext)
1617 1617 {
1618 1618 struct buf *bp = NULL;
1619 1619 int offset = 0;
1620 1620 int match = 0;
1621 1621 struct pcdir *ep;
1622 1622 struct vnode *vp = PCTOV(dp);
1623 1623 struct pcfs *fsp = VFSTOPCFS(vp->v_vfsp);
1624 1624 int boff;
1625 1625 int error = 0;
1626 1626
1627 1627 for (;;) {
1628 1628 boff = pc_blkoff(fsp, offset);
1629 1629 if (boff == 0 || bp == NULL || boff >= bp->b_bcount) {
1630 1630 if (bp != NULL) {
1631 1631 brelse(bp);
1632 1632 bp = NULL;
1633 1633 }
1634 1634 error = pc_blkatoff(dp, offset, &bp, &ep);
1635 1635 if (error == ENOENT)
1636 1636 break;
1637 1637 if (error) {
1638 1638 return (1);
1639 1639 }
1640 1640 }
1641 1641 if (PCDL_IS_LFN(ep) ||
1642 1642 (ep->pcd_filename[0] == PCD_ERASED)) {
1643 1643 offset += sizeof (struct pcdir);
1644 1644 ep++;
1645 1645 continue;
1646 1646 }
1647 1647 if (ep->pcd_filename[0] == PCD_UNUSED)
1648 1648 break;
1649 1649 /*
1650 1650 * in use, and a short file name (either standalone
1651 1651 * or associated with a long name
1652 1652 */
1653 1653 if ((bcmp(fname, ep->pcd_filename, PCFNAMESIZE) == 0) &&
1654 1654 (bcmp(fext, ep->pcd_ext, PCFEXTSIZE) == 0)) {
1655 1655 match = 1;
1656 1656 break;
1657 1657 }
1658 1658 offset += sizeof (struct pcdir);
1659 1659 ep++;
1660 1660 }
1661 1661 if (bp) {
1662 1662 brelse(bp);
1663 1663 bp = NULL;
1664 1664 }
1665 1665 return (match);
1666 1666 }
1667 1667
1668 1668 pc_cluster32_t
1669 1669 pc_getstartcluster(struct pcfs *fsp, struct pcdir *ep)
1670 1670 {
1671 1671 if (IS_FAT32(fsp)) {
1672 1672 pc_cluster32_t cn;
1673 1673 pc_cluster16_t hi16;
1674 1674 pc_cluster16_t lo16;
1675 1675
1676 1676 hi16 = ltohs(ep->un.pcd_scluster_hi);
1677 1677 lo16 = ltohs(ep->pcd_scluster_lo);
1678 1678 cn = (hi16 << 16) | lo16;
1679 1679 return (cn);
1680 1680 } else {
1681 1681 return (ltohs(ep->pcd_scluster_lo));
1682 1682 }
1683 1683 }
1684 1684
1685 1685 void
1686 1686 pc_setstartcluster(struct pcfs *fsp, struct pcdir *ep, pc_cluster32_t cln)
1687 1687 {
1688 1688 if (IS_FAT32(fsp)) {
1689 1689 pc_cluster16_t hi16;
1690 1690 pc_cluster16_t lo16;
1691 1691
1692 1692 hi16 = (cln >> 16) & 0xFFFF;
1693 1693 lo16 = cln & 0xFFFF;
1694 1694 ep->un.pcd_scluster_hi = htols(hi16);
1695 1695 ep->pcd_scluster_lo = htols(lo16);
1696 1696 } else {
1697 1697 pc_cluster16_t cln16;
1698 1698
1699 1699 cln16 = (pc_cluster16_t)cln;
1700 1700 ep->pcd_scluster_lo = htols(cln16);
1701 1701 }
1702 1702 }
|
↓ open down ↓ |
1702 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX