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