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