• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*  Copyright 1995-1998,2000-2003,2005,2007-2009 Alain Knaff.
2  *  This file is part of mtools.
3  *
4  *  Mtools is free software: you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation, either version 3 of the License, or
7  *  (at your option) any later version.
8  *
9  *  Mtools is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with Mtools.  If not, see <http://www.gnu.org/licenses/>.
16  *
17  * mk_direntry.c
18  * Make new directory entries, and handles name clashes
19  *
20  */
21 
22 /*
23  * This file is used by those commands that need to create new directory entries
24  */
25 
26 #include "sysincludes.h"
27 #include "msdos.h"
28 #include "mtools.h"
29 #include "vfat.h"
30 #include "nameclash.h"
31 #include "fs.h"
32 #include "stream.h"
33 #include "mainloop.h"
34 #include "file_name.h"
35 
36 /**
37  * Converts input to shortname
38  * @param un unix name (in Unix charset)
39  *
40  * @return 1 if name had to be mangled
41  */
convert_to_shortname(doscp_t * cp,ClashHandling_t * ch,const char * un,dos_name_t * dn)42 static __inline__ int convert_to_shortname(doscp_t *cp, ClashHandling_t *ch,
43 					   const char *un, dos_name_t *dn)
44 {
45 	int mangled;
46 
47 	/* Then do conversion to dn */
48 	ch->name_converter(cp, un, 0, &mangled, dn);
49 	dn->sentinel = '\0';
50 	if (dn->base[0] == '\xE5')
51 		dn->base[0] = '\x05';
52 	return mangled;
53 }
54 
chomp(char * line)55 static __inline__ void chomp(char *line)
56 {
57 	int l = strlen(line);
58 	while(l > 0 && (line[l-1] == '\n' || line[l-1] == '\r')) {
59 		line[--l] = '\0';
60 	}
61 }
62 
63 /**
64  * Asks for an alternative new name for a file, in case of a clash
65  */
ask_rename(doscp_t * cp,ClashHandling_t * ch,dos_name_t * shortname,char * longname,int isprimary)66 static __inline__ int ask_rename(doscp_t *cp, ClashHandling_t *ch,
67 				 dos_name_t *shortname,
68 				 char *longname,
69 				 int isprimary)
70 {
71 	int mangled;
72 
73 	/* TODO: Would be nice to suggest "autorenamed" version of name, press
74 	 * <Return> to get it.
75 	 */
76 #if 0
77 	fprintf(stderr,"Entering ask_rename, isprimary=%d.\n", isprimary);
78 #endif
79 
80 	if(!opentty(0))
81 		return 0;
82 
83 	mangled = 0;
84 	do {
85 		char tname[4*MAX_VNAMELEN+1];
86 		fprintf(stderr, "New %s name for \"%s\": ",
87 			isprimary ? "primary" : "secondary", longname);
88 		fflush(stderr);
89 		if (! fgets(tname, 4*MAX_VNAMELEN+1, opentty(0)))
90 			return 0;
91 		chomp(tname);
92 		if (isprimary)
93 			strcpy(longname, tname);
94 		else
95 			mangled = convert_to_shortname(cp,
96 						       ch, tname, shortname);
97 	} while (mangled & 1);
98 	return 1;
99 }
100 
101 /**
102  * This function determines the action to be taken in case there is a problem
103  * with target name (clash, illegal characters, or reserved)
104  * The decision either comes from the default (ch), or the user will be
105  * prompted if there is no default
106  */
ask_namematch(doscp_t * cp,dos_name_t * dosname,char * longname,int isprimary,ClashHandling_t * ch,int no_overwrite,int reason)107 static __inline__ clash_action ask_namematch(doscp_t *cp,
108 					     dos_name_t *dosname,
109 					     char *longname,
110 					     int isprimary,
111 					     ClashHandling_t *ch,
112 					     int no_overwrite,
113 					     int reason)
114 {
115 	/* User's answer letter (from keyboard). Only first letter is used,
116 	 * but we allocate space for 10 in order to account for extra garbage
117 	 * that user may enter
118 	 */
119 	char ans[10];
120 
121 	/**
122 	 * Return value: action to be taken
123 	 */
124 	clash_action a;
125 
126 	/**
127 	 * Should this decision be made permanent (do no longer ask same
128 	 * question)
129 	 */
130 	int perm;
131 
132 	/**
133 	 * Buffer for shortname
134 	 */
135 	char name_buffer[4*13];
136 
137 	/**
138 	 * Name to be printed
139 	 */
140 	char *name;
141 
142 #define EXISTS 0
143 #define RESERVED 1
144 #define ILLEGALS 2
145 
146 	static const char *reasons[]= {
147 		"already exists",
148 		"is reserved",
149 		"contains illegal character(s)"};
150 
151 	a = ch->action[isprimary];
152 
153 	if(a == NAMEMATCH_NONE && !opentty(1)) {
154 		/* no default, and no tty either . Skip the troublesome file */
155 		return NAMEMATCH_SKIP;
156 	}
157 
158 	if (!isprimary)
159 		name = unix_normalize(cp, name_buffer,
160 				      dosname, sizeof(*dosname));
161 	else
162 		name = longname;
163 
164 	perm = 0;
165 	while (a == NAMEMATCH_NONE) {
166 		fprintf(stderr, "%s file name \"%s\" %s.\n",
167 			isprimary ? "Long" : "Short", name, reasons[reason]);
168 		fprintf(stderr,
169 			"a)utorename A)utorename-all r)ename R)ename-all ");
170 		if(!no_overwrite)
171 			fprintf(stderr,"o)verwrite O)verwrite-all");
172 		fprintf(stderr,
173 			"\ns)kip S)kip-all q)uit (aArR");
174 		if(!no_overwrite)
175 			fprintf(stderr,"oO");
176 		fprintf(stderr,"sSq): ");
177 		fflush(stderr);
178 		fflush(opentty(1));
179 		if (mtools_raw_tty) {
180 			int rep;
181 			rep = fgetc(opentty(1));
182 			fputs("\n", stderr);
183 			if(rep == EOF)
184 				ans[0] = 'q';
185 			else
186 				ans[0] = rep;
187 		} else {
188 			if(fgets(ans, 9, opentty(0)) == NULL)
189 				ans[0] = 'q';
190 		}
191 		perm = isupper((unsigned char)ans[0]);
192 		switch(tolower((unsigned char)ans[0])) {
193 			case 'a':
194 				a = NAMEMATCH_AUTORENAME;
195 				break;
196 			case 'r':
197 				if(isprimary)
198 					a = NAMEMATCH_PRENAME;
199 				else
200 					a = NAMEMATCH_RENAME;
201 				break;
202 			case 'o':
203 				if(no_overwrite)
204 					continue;
205 				a = NAMEMATCH_OVERWRITE;
206 				break;
207 			case 's':
208 				a = NAMEMATCH_SKIP;
209 				break;
210 			case 'q':
211 				perm = 0;
212 				a = NAMEMATCH_QUIT;
213 				break;
214 			default:
215 				perm = 0;
216 		}
217 	}
218 
219 	/* Keep track of this action in case this file collides again */
220 	ch->action[isprimary]  = a;
221 	if (perm)
222 		ch->namematch_default[isprimary] = a;
223 
224 	/* if we were asked to overwrite be careful. We can't set the action
225 	 * to overwrite, else we get won't get a chance to specify another
226 	 * action, should overwrite fail. Indeed, we'll be caught in an
227 	 * infinite loop because overwrite will fail the same way for the
228 	 * second time */
229 	if(a == NAMEMATCH_OVERWRITE)
230 		ch->action[isprimary] = NAMEMATCH_NONE;
231 	return a;
232 }
233 
234 /*
235  * Processes a name match
236  *  dosname short dosname (ignored if is_primary)
237  *
238  *
239  * Returns:
240  * 2 if file is to be overwritten
241  * 1 if file was renamed
242  * 0 if it was skipped
243  *
244  * If a short name is involved, handle conversion between the 11-character
245  * fixed-length record DOS name and a literal null-terminated name (e.g.
246  * "COMMAND  COM" (no null) <-> "COMMAND.COM" (null terminated)).
247  *
248  * Also, immediately copy the original name so that messages can use it.
249  */
process_namematch(doscp_t * cp,dos_name_t * dosname,char * longname,int isprimary,ClashHandling_t * ch,int no_overwrite,int reason)250 static __inline__ clash_action process_namematch(doscp_t *cp,
251 						 dos_name_t *dosname,
252 						 char *longname,
253 						 int isprimary,
254 						 ClashHandling_t *ch,
255 						 int no_overwrite,
256 						 int reason)
257 {
258 	clash_action action;
259 
260 #if 0
261 	fprintf(stderr,
262 		"process_namematch: name=%s, default_action=%d, ask=%d.\n",
263 		name, default_action, ch->ask);
264 #endif
265 
266 	action = ask_namematch(cp, dosname, longname,
267 			       isprimary, ch, no_overwrite, reason);
268 
269 	switch(action){
270 	case NAMEMATCH_QUIT:
271 		got_signal = 1;
272 		return NAMEMATCH_SKIP;
273 	case NAMEMATCH_SKIP:
274 		return NAMEMATCH_SKIP;
275 	case NAMEMATCH_RENAME:
276 	case NAMEMATCH_PRENAME:
277 		/* We need to rename the file now.  This means we must pass
278 		 * back through the loop, a) ensuring there isn't a potential
279 		 * new name collision, and b) finding a big enough VSE.
280 		 * Change the name, so that it won't collide again.
281 		 */
282 		ask_rename(cp, ch, dosname, longname, isprimary);
283 		return action;
284 	case NAMEMATCH_AUTORENAME:
285 		/* Very similar to NAMEMATCH_RENAME, except that we need to
286 		 * first generate the name.
287 		 * TODO: Remember previous name so we don't
288 		 * keep trying the same one.
289 		 */
290 		if (isprimary) {
291 			autorename_long(longname, 1);
292 			return NAMEMATCH_PRENAME;
293 		} else {
294 			autorename_short(dosname, 1);
295 			return NAMEMATCH_RENAME;
296 		}
297 	case NAMEMATCH_OVERWRITE:
298 		if(no_overwrite)
299 			return NAMEMATCH_SKIP;
300 		else
301 			return NAMEMATCH_OVERWRITE;
302 	default:
303 		return NAMEMATCH_NONE;
304 	}
305 }
306 
contains_illegals(const char * string,const char * illegals,int len)307 static int contains_illegals(const char *string, const char *illegals,
308 			     int len)
309 {
310 	for(; *string && len--; string++)
311 		if((*string < ' ' && *string != '\005' && !(*string & 0x80)) ||
312 		   strchr(illegals, *string))
313 			return 1;
314 	return 0;
315 }
316 
is_reserved(char * ans,int islong)317 static int is_reserved(char *ans, int islong)
318 {
319 	unsigned int i;
320 	static const char *dev3[] = {"CON", "AUX", "PRN", "NUL", "   "};
321 	static const char *dev4[] = {"COM", "LPT" };
322 
323 	for (i = 0; i < sizeof(dev3)/sizeof(*dev3); i++)
324 		if (!strncasecmp(ans, dev3[i], 3) &&
325 		    ((islong && !ans[3]) ||
326 		     (!islong && !strncmp(ans+3,"     ",5))))
327 			return 1;
328 
329 	for (i = 0; i < sizeof(dev4)/sizeof(*dev4); i++)
330 		if (!strncasecmp(ans, dev4[i], 3) &&
331 		    (ans[3] >= '1' && ans[3] <= '4') &&
332 		    ((islong && !ans[4]) ||
333 		     (!islong && !strncmp(ans+4,"    ",4))))
334 			return 1;
335 
336 	return 0;
337 }
338 
get_slots(Stream_t * Dir,dos_name_t * dosname,char * longname,struct scan_state * ssp,ClashHandling_t * ch)339 static __inline__ clash_action get_slots(Stream_t *Dir,
340 					 dos_name_t *dosname,
341 					 char *longname,
342 					 struct scan_state *ssp,
343 					 ClashHandling_t *ch)
344 {
345 	int error;
346 	clash_action ret;
347 	int match_pos=0;
348 	direntry_t entry;
349 	int isprimary;
350 	int no_overwrite;
351 	int reason;
352 	int pessimisticShortRename;
353 	doscp_t *cp = GET_DOSCONVERT(Dir);
354 
355 	pessimisticShortRename = (ch->action[0] == NAMEMATCH_AUTORENAME);
356 
357 	entry.Dir = Dir;
358 	no_overwrite = 1;
359 	if((is_reserved(longname,1)) ||
360 	   longname[strspn(longname,". ")] == '\0'){
361 		reason = RESERVED;
362 		isprimary = 1;
363 	} else if(contains_illegals(longname,long_illegals,1024)) {
364 		reason = ILLEGALS;
365 		isprimary = 1;
366 	} else if(is_reserved(dosname->base,0)) {
367 		reason = RESERVED;
368 		ch->use_longname = 1;
369 		isprimary = 0;
370 	} else if(!ch->is_label &&
371 		  contains_illegals(dosname->base,short_illegals,11)) {
372 		reason = ILLEGALS;
373 		ch->use_longname = 1;
374 		isprimary = 0;
375 	} else {
376 		reason = EXISTS;
377 		switch (lookupForInsert(Dir,
378 					&entry,
379 					dosname, longname, ssp,
380 					ch->ignore_entry,
381 					ch->source_entry,
382 					pessimisticShortRename &&
383 					ch->use_longname,
384 					ch->use_longname)) {
385 			case -1:
386 				return NAMEMATCH_ERROR;
387 
388 			case 0:
389 				return NAMEMATCH_SKIP;
390 				/* Single-file error error or skip request */
391 
392 			case 5:
393 				return NAMEMATCH_GREW;
394 				/* Grew directory, try again */
395 
396 			case 6:
397 				return NAMEMATCH_SUCCESS; /* Success */
398 		}
399 		match_pos = -2;
400 		if (ssp->longmatch > -1) {
401 			/* Primary Long Name Match */
402 #ifdef debug
403 			fprintf(stderr,
404 				"Got longmatch=%d for name %s.\n",
405 				longmatch, longname);
406 #endif
407 			match_pos = ssp->longmatch;
408 			isprimary = 1;
409 		} else if ((ch->use_longname & 1) && (ssp->shortmatch != -1)) {
410 			/* Secondary Short Name Match */
411 #ifdef debug
412 			fprintf(stderr,
413 				"Got secondary short name match for name %s.\n",
414 				longname);
415 #endif
416 
417 			match_pos = ssp->shortmatch;
418 			isprimary = 0;
419 		} else if (ssp->shortmatch >= 0) {
420 			/* Primary Short Name Match */
421 #ifdef debug
422 			fprintf(stderr,
423 				"Got primary short name match for name %s.\n",
424 				longname);
425 #endif
426 			match_pos = ssp->shortmatch;
427 			isprimary = 1;
428 		} else
429 			return NAMEMATCH_RENAME;
430 
431 		if(match_pos > -1) {
432 			entry.entry = match_pos;
433 			dir_read(&entry, &error);
434 			if (error)
435 			    return NAMEMATCH_ERROR;
436 			/* if we can't overwrite, don't propose it */
437 			no_overwrite = (match_pos == ch->source || IS_DIR(&entry));
438 		}
439 	}
440 	ret = process_namematch(cp, dosname, longname,
441 				isprimary, ch, no_overwrite, reason);
442 
443 	if (ret == NAMEMATCH_OVERWRITE && match_pos > -1){
444 		if((entry.dir.attr & 0x5) &&
445 		   (ask_confirmation("file is read only, overwrite anyway (y/n) ? ")))
446 			return NAMEMATCH_RENAME;
447 		/* Free up the file to be overwritten */
448 		if(fatFreeWithDirentry(&entry))
449 			return NAMEMATCH_ERROR;
450 
451 #if 0
452 		if(isprimary &&
453 		   match_pos - ssp->match_free + 1 >= ssp->size_needed){
454 			/* reuse old entry and old short name for overwrite */
455 			ssp->free_start = match_pos - ssp->size_needed + 1;
456 			ssp->free_size = ssp->size_needed;
457 			ssp->slot = match_pos;
458 			ssp->got_slots = 1;
459 			strncpy(dosname, dir.name, 3);
460 			strncpy(dosname + 8, dir.ext, 3);
461 			return ret;
462 		} else
463 #endif
464 			{
465 			wipeEntry(&entry);
466 			return NAMEMATCH_RENAME;
467 		}
468 	}
469 
470 	return ret;
471 }
472 
473 
write_slots(Stream_t * Dir,dos_name_t * dosname,char * longname,struct scan_state * ssp,write_data_callback * cb,void * arg,int Case)474 static __inline__ int write_slots(Stream_t *Dir,
475 				  dos_name_t *dosname,
476 				  char *longname,
477 				  struct scan_state *ssp,
478 				  write_data_callback *cb,
479 				  void *arg,
480 				  int Case)
481 {
482 	direntry_t entry;
483 
484 	/* write the file */
485 	if (fat_error(Dir))
486 		return 0;
487 
488 	entry.Dir = Dir;
489 	entry.entry = ssp->slot;
490 	native_to_wchar(longname, entry.name, MAX_VNAMELEN, 0, 0);
491 	entry.name[MAX_VNAMELEN]='\0';
492 	entry.dir.Case = Case & (EXTCASE | BASECASE);
493 	if (cb(dosname, longname, arg, &entry) >= 0) {
494 		if ((ssp->size_needed > 1) &&
495 		    (ssp->free_end - ssp->free_start >= ssp->size_needed)) {
496 			ssp->slot = write_vfat(Dir, dosname, longname,
497 					       ssp->free_start, &entry);
498 		} else {
499 			ssp->size_needed = 1;
500 			write_vfat(Dir, dosname, 0,
501 				   ssp->free_start, &entry);
502 		}
503 		/* clear_vses(Dir, ssp->free_start + ssp->size_needed,
504 		   ssp->free_end); */
505 	} else
506 		return 0;
507 
508 	return 1;	/* Successfully wrote the file */
509 }
510 
stripspaces(char * name)511 static void stripspaces(char *name)
512 {
513 	char *p,*non_space;
514 
515 	non_space = name;
516 	for(p=name; *p; p++)
517 		if (*p != ' ')
518 			non_space = p;
519 	if(name[0])
520 		non_space[1] = '\0';
521 }
522 
523 
_mwrite_one(Stream_t * Dir,char * argname,char * shortname,write_data_callback * cb,void * arg,ClashHandling_t * ch)524 static int _mwrite_one(Stream_t *Dir,
525 		       char *argname,
526 		       char *shortname,
527 		       write_data_callback *cb,
528 		       void *arg,
529 		       ClashHandling_t *ch)
530 {
531 	char longname[VBUFSIZE];
532 	const char *dstname;
533 	dos_name_t dosname;
534 	int expanded;
535 	struct scan_state scan;
536 	clash_action ret;
537 	doscp_t *cp = GET_DOSCONVERT(Dir);
538 
539 	expanded = 0;
540 
541 	if(isSpecial(argname)) {
542 		fprintf(stderr, "Cannot create entry named . or ..\n");
543 		return -1;
544 	}
545 
546 	if(ch->name_converter == dos_name) {
547 		if(shortname)
548 			stripspaces(shortname);
549 		if(argname)
550 			stripspaces(argname);
551 	}
552 
553 	if(shortname){
554 		convert_to_shortname(cp, ch, shortname, &dosname);
555 		if(ch->use_longname & 1){
556 			/* short name mangled, treat it as a long name */
557 			argname = shortname;
558 			shortname = 0;
559 		}
560 	}
561 
562 	if (argname[0] && (argname[1] == ':')) {
563 		/* Skip drive letter */
564 		dstname = argname + 2;
565 	} else {
566 		dstname = argname;
567 	}
568 
569 	/* Copy original argument dstname to working value longname */
570 	strncpy(longname, dstname, VBUFSIZE-1);
571 
572 	if(shortname) {
573 		ch->use_longname =
574 			convert_to_shortname(cp, ch, shortname, &dosname);
575 		if(strcmp(shortname, longname))
576 			ch->use_longname |= 1;
577 	} else {
578 		ch->use_longname =
579 			convert_to_shortname(cp, ch, longname, &dosname);
580 	}
581 
582 	ch->action[0] = ch->namematch_default[0];
583 	ch->action[1] = ch->namematch_default[1];
584 
585 	while (1) {
586 		switch((ret=get_slots(Dir, &dosname, longname, &scan, ch))){
587 			case NAMEMATCH_ERROR:
588 				return -1;	/* Non-file-specific error,
589 						 * quit */
590 
591 			case NAMEMATCH_SKIP:
592 				return -1;	/* Skip file (user request or
593 						 * error) */
594 
595 			case NAMEMATCH_PRENAME:
596 				ch->use_longname =
597 					convert_to_shortname(cp, ch,
598 							     longname,
599 							     &dosname);
600 				continue;
601 			case NAMEMATCH_RENAME:
602 				continue;	/* Renamed file, loop again */
603 
604 			case NAMEMATCH_GREW:
605 				/* No collision, and not enough slots.
606 				 * Try to grow the directory
607 				 */
608 				if (expanded) {	/* Already tried this
609 						 * once, no good */
610 					fprintf(stderr,
611 						"%s: No directory slots\n",
612 						progname);
613 					return -1;
614 				}
615 				expanded = 1;
616 
617 				if (dir_grow(Dir, scan.max_entry))
618 					return -1;
619 				continue;
620 			case NAMEMATCH_OVERWRITE:
621 			case NAMEMATCH_SUCCESS:
622 				return write_slots(Dir, &dosname, longname,
623 						   &scan, cb, arg,
624 						   ch->use_longname);
625 			default:
626 				fprintf(stderr,
627 					"Internal error: clash_action=%d\n",
628 					ret);
629 				return -1;
630 		}
631 
632 	}
633 }
634 
mwrite_one(Stream_t * Dir,const char * _argname,const char * _shortname,write_data_callback * cb,void * arg,ClashHandling_t * ch)635 int mwrite_one(Stream_t *Dir,
636 	       const char *_argname,
637 	       const char *_shortname,
638 	       write_data_callback *cb,
639 	       void *arg,
640 	       ClashHandling_t *ch)
641 {
642 	char *argname;
643 	char *shortname;
644 	int ret;
645 
646 	if(_argname)
647 		argname = strdup(_argname);
648 	else
649 		argname = 0;
650 	if(_shortname)
651 		shortname = strdup(_shortname);
652 	else
653 		shortname = 0;
654 	ret = _mwrite_one(Dir, argname, shortname, cb, arg, ch);
655 	if(argname)
656 		free(argname);
657 	if(shortname)
658 		free(shortname);
659 	return ret;
660 }
661 
init_clash_handling(ClashHandling_t * ch)662 void init_clash_handling(ClashHandling_t *ch)
663 {
664 	ch->ignore_entry = -1;
665 	ch->source_entry = -2;
666 	ch->nowarn = 0;	/*Don't ask, just do default action if name collision */
667 	ch->namematch_default[0] = NAMEMATCH_AUTORENAME;
668 	ch->namematch_default[1] = NAMEMATCH_NONE;
669 	ch->name_converter = dos_name; /* changed by mlabel */
670 	ch->source = -2;
671 	ch->is_label = 0;
672 }
673 
handle_clash_options(ClashHandling_t * ch,char c)674 int handle_clash_options(ClashHandling_t *ch, char c)
675 {
676 	int isprimary;
677 	if(isupper(c))
678 		isprimary = 0;
679 	else
680 		isprimary = 1;
681 	c = ch_tolower(c);
682 	switch(c) {
683 		case 'o':
684 			/* Overwrite if primary name matches */
685 			ch->namematch_default[isprimary] = NAMEMATCH_OVERWRITE;
686 			return 0;
687 		case 'r':
688 				/* Rename primary name interactively */
689 			ch->namematch_default[isprimary] = NAMEMATCH_RENAME;
690 			return 0;
691 		case 's':
692 			/* Skip file if primary name collides */
693 			ch->namematch_default[isprimary] = NAMEMATCH_SKIP;
694 			return 0;
695 		case 'm':
696 			ch->namematch_default[isprimary] = NAMEMATCH_NONE;
697 			return 0;
698 		case 'a':
699 			ch->namematch_default[isprimary] = NAMEMATCH_AUTORENAME;
700 			return 0;
701 		default:
702 			return -1;
703 	}
704 }
705 
dosnameToDirentry(const struct dos_name_t * dn,struct directory * dir)706 void dosnameToDirentry(const struct dos_name_t *dn, struct directory *dir) {
707 	strncpy(dir->name, dn->base, 8);
708 	strncpy(dir->ext, dn->ext, 3);
709 }
710