xref: /aosp_15_r20/external/mtools/mlabel.c (revision d5c9a868b113e0ec0db2f27bc2ce8a253e77c4b0)
1*d5c9a868SElliott Hughes /*  Copyright 1986-1992 Emmet P. Gray.
2*d5c9a868SElliott Hughes  *  Copyright 1996-1998,2000-2002,2005,2007-2009 Alain Knaff.
3*d5c9a868SElliott Hughes  *  This file is part of mtools.
4*d5c9a868SElliott Hughes  *
5*d5c9a868SElliott Hughes  *  Mtools is free software: you can redistribute it and/or modify
6*d5c9a868SElliott Hughes  *  it under the terms of the GNU General Public License as published by
7*d5c9a868SElliott Hughes  *  the Free Software Foundation, either version 3 of the License, or
8*d5c9a868SElliott Hughes  *  (at your option) any later version.
9*d5c9a868SElliott Hughes  *
10*d5c9a868SElliott Hughes  *  Mtools is distributed in the hope that it will be useful,
11*d5c9a868SElliott Hughes  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12*d5c9a868SElliott Hughes  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13*d5c9a868SElliott Hughes  *  GNU General Public License for more details.
14*d5c9a868SElliott Hughes  *
15*d5c9a868SElliott Hughes  *  You should have received a copy of the GNU General Public License
16*d5c9a868SElliott Hughes  *  along with Mtools.  If not, see <http://www.gnu.org/licenses/>.
17*d5c9a868SElliott Hughes  *
18*d5c9a868SElliott Hughes  * mlabel.c
19*d5c9a868SElliott Hughes  * Make an MSDOS volume label
20*d5c9a868SElliott Hughes  */
21*d5c9a868SElliott Hughes 
22*d5c9a868SElliott Hughes #include "sysincludes.h"
23*d5c9a868SElliott Hughes #include "msdos.h"
24*d5c9a868SElliott Hughes #include "mainloop.h"
25*d5c9a868SElliott Hughes #include "vfat.h"
26*d5c9a868SElliott Hughes #include "mtools.h"
27*d5c9a868SElliott Hughes #include "nameclash.h"
28*d5c9a868SElliott Hughes #include "file_name.h"
29*d5c9a868SElliott Hughes 
_label_name(doscp_t * cp,const char * filename,int verbose UNUSEDP,int * mangled,dos_name_t * ans,int preserve_case)30*d5c9a868SElliott Hughes static void _label_name(doscp_t *cp, const char *filename, int verbose UNUSEDP,
31*d5c9a868SElliott Hughes 			int *mangled, dos_name_t *ans, int preserve_case)
32*d5c9a868SElliott Hughes {
33*d5c9a868SElliott Hughes 	size_t len;
34*d5c9a868SElliott Hughes 	size_t i;
35*d5c9a868SElliott Hughes 	int have_lower, have_upper;
36*d5c9a868SElliott Hughes 	wchar_t wbuffer[12];
37*d5c9a868SElliott Hughes 
38*d5c9a868SElliott Hughes 	memset(ans, ' ', sizeof(*ans)-1);
39*d5c9a868SElliott Hughes 	ans->sentinel = '\0';
40*d5c9a868SElliott Hughes 	len = native_to_wchar(filename, wbuffer, 11, 0, 0);
41*d5c9a868SElliott Hughes 	if(len > 11){
42*d5c9a868SElliott Hughes 		*mangled = 1;
43*d5c9a868SElliott Hughes 		len = 11;
44*d5c9a868SElliott Hughes 	} else
45*d5c9a868SElliott Hughes 		*mangled = 0;
46*d5c9a868SElliott Hughes 
47*d5c9a868SElliott Hughes 	have_lower = have_upper = 0;
48*d5c9a868SElliott Hughes 	for(i=0; i<len; i++){
49*d5c9a868SElliott Hughes 		if(islower(wbuffer[i]))
50*d5c9a868SElliott Hughes 			have_lower = 1;
51*d5c9a868SElliott Hughes 		if(isupper(wbuffer[i]))
52*d5c9a868SElliott Hughes 			have_upper = 1;
53*d5c9a868SElliott Hughes 		if(!preserve_case)
54*d5c9a868SElliott Hughes 			wbuffer[i] = ch_towupper(wbuffer[i]);
55*d5c9a868SElliott Hughes 		if(
56*d5c9a868SElliott Hughes #ifdef HAVE_WCHAR_H
57*d5c9a868SElliott Hughes 		   wcschr(L"^+=/[]:,?*\\<>|\".", wbuffer[i])
58*d5c9a868SElliott Hughes #else
59*d5c9a868SElliott Hughes 		   strchr("^+=/[]:,?*\\<>|\".", wbuffer[i])
60*d5c9a868SElliott Hughes #endif
61*d5c9a868SElliott Hughes 		   ){
62*d5c9a868SElliott Hughes 			*mangled = 1;
63*d5c9a868SElliott Hughes 			wbuffer[i] = '~';
64*d5c9a868SElliott Hughes 		}
65*d5c9a868SElliott Hughes 	}
66*d5c9a868SElliott Hughes 	if (have_lower && have_upper)
67*d5c9a868SElliott Hughes 		*mangled = 1;
68*d5c9a868SElliott Hughes 	wchar_to_dos(cp, wbuffer, ans->base, len, mangled);
69*d5c9a868SElliott Hughes }
70*d5c9a868SElliott Hughes 
label_name_uc(doscp_t * cp,const char * filename,int verbose,int * mangled,dos_name_t * ans)71*d5c9a868SElliott Hughes void label_name_uc(doscp_t *cp, const char *filename, int verbose,
72*d5c9a868SElliott Hughes 		   int *mangled, dos_name_t *ans)
73*d5c9a868SElliott Hughes {
74*d5c9a868SElliott Hughes 	_label_name(cp, filename, verbose, mangled, ans, 0);
75*d5c9a868SElliott Hughes }
76*d5c9a868SElliott Hughes 
label_name_pc(doscp_t * cp,const char * filename,int verbose,int * mangled,dos_name_t * ans)77*d5c9a868SElliott Hughes void label_name_pc(doscp_t *cp, const char *filename, int verbose,
78*d5c9a868SElliott Hughes 		   int *mangled, dos_name_t *ans)
79*d5c9a868SElliott Hughes {
80*d5c9a868SElliott Hughes 	_label_name(cp, filename, verbose, mangled, ans, 1);
81*d5c9a868SElliott Hughes }
82*d5c9a868SElliott Hughes 
labelit(struct dos_name_t * dosname,char * longname UNUSEDP,void * arg0 UNUSEDP,direntry_t * entry)83*d5c9a868SElliott Hughes int labelit(struct dos_name_t *dosname,
84*d5c9a868SElliott Hughes 	    char *longname UNUSEDP,
85*d5c9a868SElliott Hughes 	    void *arg0 UNUSEDP,
86*d5c9a868SElliott Hughes 	    direntry_t *entry)
87*d5c9a868SElliott Hughes {
88*d5c9a868SElliott Hughes 	time_t now;
89*d5c9a868SElliott Hughes 
90*d5c9a868SElliott Hughes 	/* find out current time */
91*d5c9a868SElliott Hughes 	getTimeNow(&now);
92*d5c9a868SElliott Hughes 	mk_entry(dosname, 0x8, 0, 0, now, &entry->dir);
93*d5c9a868SElliott Hughes 	return 0;
94*d5c9a868SElliott Hughes }
95*d5c9a868SElliott Hughes 
96*d5c9a868SElliott Hughes static void usage(int ret) NORETURN;
usage(int ret)97*d5c9a868SElliott Hughes static void usage(int ret)
98*d5c9a868SElliott Hughes {
99*d5c9a868SElliott Hughes 	fprintf(stderr, "Mtools version %s, dated %s\n",
100*d5c9a868SElliott Hughes 		mversion, mdate);
101*d5c9a868SElliott Hughes 	fprintf(stderr, "Usage: %s [-vscVn] [-N serial] drive:\n", progname);
102*d5c9a868SElliott Hughes 	exit(ret);
103*d5c9a868SElliott Hughes }
104*d5c9a868SElliott Hughes 
105*d5c9a868SElliott Hughes 
106*d5c9a868SElliott Hughes void mlabel(int argc, char **argv, int type UNUSEDP) NORETURN;
mlabel(int argc,char ** argv,int type UNUSEDP)107*d5c9a868SElliott Hughes void mlabel(int argc, char **argv, int type UNUSEDP)
108*d5c9a868SElliott Hughes {
109*d5c9a868SElliott Hughes 
110*d5c9a868SElliott Hughes 	const char *newLabel="";
111*d5c9a868SElliott Hughes 	int verbose, clear, interactive, show;
112*d5c9a868SElliott Hughes 	direntry_t entry;
113*d5c9a868SElliott Hughes 	int result=0;
114*d5c9a868SElliott Hughes 	char longname[VBUFSIZE];
115*d5c9a868SElliott Hughes 	char shortname[45];
116*d5c9a868SElliott Hughes 	ClashHandling_t ch;
117*d5c9a868SElliott Hughes 	struct MainParam_t mp;
118*d5c9a868SElliott Hughes 	Stream_t *RootDir;
119*d5c9a868SElliott Hughes 	int c;
120*d5c9a868SElliott Hughes 	int mangled;
121*d5c9a868SElliott Hughes 	enum { SER_NONE, SER_RANDOM, SER_SET }  set_serial = SER_NONE;
122*d5c9a868SElliott Hughes 	uint32_t serial = 0;
123*d5c9a868SElliott Hughes 	int need_write_boot = 0;
124*d5c9a868SElliott Hughes 	int have_boot = 0;
125*d5c9a868SElliott Hughes 	char *eptr;
126*d5c9a868SElliott Hughes 	union bootsector boot;
127*d5c9a868SElliott Hughes 	Stream_t *Fs=0;
128*d5c9a868SElliott Hughes 	int r;
129*d5c9a868SElliott Hughes 	struct label_blk_t *labelBlock;
130*d5c9a868SElliott Hughes 	int isRo=0;
131*d5c9a868SElliott Hughes 	int *isRop=NULL;
132*d5c9a868SElliott Hughes 	char drive;
133*d5c9a868SElliott Hughes 
134*d5c9a868SElliott Hughes 	init_clash_handling(&ch);
135*d5c9a868SElliott Hughes 	ch.name_converter = label_name_uc;
136*d5c9a868SElliott Hughes 	ch.ignore_entry = -2;
137*d5c9a868SElliott Hughes 	ch.is_label = 1;
138*d5c9a868SElliott Hughes 
139*d5c9a868SElliott Hughes 	verbose = 0;
140*d5c9a868SElliott Hughes 	clear = 0;
141*d5c9a868SElliott Hughes 	show = 0;
142*d5c9a868SElliott Hughes 
143*d5c9a868SElliott Hughes 	if(helpFlag(argc, argv))
144*d5c9a868SElliott Hughes 		usage(0);
145*d5c9a868SElliott Hughes 	while ((c = getopt(argc, argv, "i:vcsnN:h")) != EOF) {
146*d5c9a868SElliott Hughes 		switch (c) {
147*d5c9a868SElliott Hughes 			case 'i':
148*d5c9a868SElliott Hughes 				set_cmd_line_image(optarg);
149*d5c9a868SElliott Hughes 				break;
150*d5c9a868SElliott Hughes 			case 'v':
151*d5c9a868SElliott Hughes 				verbose = 1;
152*d5c9a868SElliott Hughes 				break;
153*d5c9a868SElliott Hughes 			case 'c':
154*d5c9a868SElliott Hughes 				clear = 1;
155*d5c9a868SElliott Hughes 				break;
156*d5c9a868SElliott Hughes 			case 's':
157*d5c9a868SElliott Hughes 				show = 1;
158*d5c9a868SElliott Hughes 				break;
159*d5c9a868SElliott Hughes 			case 'n':
160*d5c9a868SElliott Hughes 				set_serial = SER_RANDOM;
161*d5c9a868SElliott Hughes 				init_random();
162*d5c9a868SElliott Hughes 				serial=(uint32_t) random();
163*d5c9a868SElliott Hughes 				break;
164*d5c9a868SElliott Hughes 			case 'N':
165*d5c9a868SElliott Hughes 				set_serial = SER_SET;
166*d5c9a868SElliott Hughes 				errno=0;
167*d5c9a868SElliott Hughes 				serial = strtou32(optarg, &eptr, 16);
168*d5c9a868SElliott Hughes 				if(*eptr) {
169*d5c9a868SElliott Hughes 					fprintf(stderr,
170*d5c9a868SElliott Hughes 						"%s not a valid serial number\n",
171*d5c9a868SElliott Hughes 						optarg);
172*d5c9a868SElliott Hughes 					exit(1);
173*d5c9a868SElliott Hughes 				}
174*d5c9a868SElliott Hughes 				check_number_parse_errno((char)c, optarg, eptr);
175*d5c9a868SElliott Hughes 				break;
176*d5c9a868SElliott Hughes 			case 'h':
177*d5c9a868SElliott Hughes 				usage(0);
178*d5c9a868SElliott Hughes 			default:
179*d5c9a868SElliott Hughes 				usage(1);
180*d5c9a868SElliott Hughes 			}
181*d5c9a868SElliott Hughes 	}
182*d5c9a868SElliott Hughes 
183*d5c9a868SElliott Hughes 	if (argc - optind > 1)
184*d5c9a868SElliott Hughes 		usage(1);
185*d5c9a868SElliott Hughes 	if(argc - optind == 1) {
186*d5c9a868SElliott Hughes 	    if(!argv[optind][0] || argv[optind][1] != ':')
187*d5c9a868SElliott Hughes 		usage(1);
188*d5c9a868SElliott Hughes 	    drive = ch_toupper(argv[argc -1][0]);
189*d5c9a868SElliott Hughes 	    newLabel = argv[optind]+2;
190*d5c9a868SElliott Hughes 	} else {
191*d5c9a868SElliott Hughes 	    drive = get_default_drive();
192*d5c9a868SElliott Hughes 	}
193*d5c9a868SElliott Hughes 
194*d5c9a868SElliott Hughes 	init_mp(&mp);
195*d5c9a868SElliott Hughes 	if(strlen(newLabel) > VBUFSIZE) {
196*d5c9a868SElliott Hughes 		fprintf(stderr, "Label too long\n");
197*d5c9a868SElliott Hughes 		FREE(&RootDir);
198*d5c9a868SElliott Hughes 		exit(1);
199*d5c9a868SElliott Hughes 	}
200*d5c9a868SElliott Hughes 
201*d5c9a868SElliott Hughes 	interactive = !show && !clear &&!newLabel[0] &&
202*d5c9a868SElliott Hughes 		(set_serial == SER_NONE);
203*d5c9a868SElliott Hughes 	if(!clear && !newLabel[0]) {
204*d5c9a868SElliott Hughes 		isRop = &isRo;
205*d5c9a868SElliott Hughes 	}
206*d5c9a868SElliott Hughes 	if(clear && newLabel[0]) {
207*d5c9a868SElliott Hughes 		/* Clear and new label specified both */
208*d5c9a868SElliott Hughes 		fprintf(stderr, "Both clear and new label specified\n");
209*d5c9a868SElliott Hughes 		FREE(&RootDir);
210*d5c9a868SElliott Hughes 		exit(1);
211*d5c9a868SElliott Hughes 	}
212*d5c9a868SElliott Hughes 	RootDir = open_root_dir(drive, isRop ? 0 : O_RDWR, isRop);
213*d5c9a868SElliott Hughes 	if(isRo) {
214*d5c9a868SElliott Hughes 		show = 1;
215*d5c9a868SElliott Hughes 		interactive = 0;
216*d5c9a868SElliott Hughes 	}
217*d5c9a868SElliott Hughes 	if(!RootDir) {
218*d5c9a868SElliott Hughes 		fprintf(stderr, "%s: Cannot initialize drive\n", argv[0]);
219*d5c9a868SElliott Hughes 		exit(1);
220*d5c9a868SElliott Hughes 	}
221*d5c9a868SElliott Hughes 
222*d5c9a868SElliott Hughes 	initializeDirentry(&entry, RootDir);
223*d5c9a868SElliott Hughes 	r=vfat_lookup(&entry, 0, 0, ACCEPT_LABEL | MATCH_ANY,
224*d5c9a868SElliott Hughes 		      shortname, sizeof(shortname),
225*d5c9a868SElliott Hughes 		      longname, sizeof(longname));
226*d5c9a868SElliott Hughes 	if (r == -2) {
227*d5c9a868SElliott Hughes 		FREE(&RootDir);
228*d5c9a868SElliott Hughes 		exit(1);
229*d5c9a868SElliott Hughes 	}
230*d5c9a868SElliott Hughes 
231*d5c9a868SElliott Hughes 	if(show || interactive){
232*d5c9a868SElliott Hughes 		if(isNotFound(&entry))
233*d5c9a868SElliott Hughes 			printf(" Volume has no label\n");
234*d5c9a868SElliott Hughes 		else if (*longname)
235*d5c9a868SElliott Hughes 			printf(" Volume label is %s (abbr=%s)\n",
236*d5c9a868SElliott Hughes 			       longname, shortname);
237*d5c9a868SElliott Hughes 		else
238*d5c9a868SElliott Hughes 			printf(" Volume label is %s\n",  shortname);
239*d5c9a868SElliott Hughes 
240*d5c9a868SElliott Hughes 	}
241*d5c9a868SElliott Hughes 
242*d5c9a868SElliott Hughes 	/* ask for new label */
243*d5c9a868SElliott Hughes 	if(interactive){
244*d5c9a868SElliott Hughes 		saved_sig_state ss;
245*d5c9a868SElliott Hughes 		newLabel = longname;
246*d5c9a868SElliott Hughes 		allow_interrupts(&ss);
247*d5c9a868SElliott Hughes 		fprintf(stderr,"Enter the new volume label : ");
248*d5c9a868SElliott Hughes 		if(fgets(longname, VBUFSIZE, stdin) == NULL) {
249*d5c9a868SElliott Hughes 			fprintf(stderr, "\n");
250*d5c9a868SElliott Hughes 			if(errno == EINTR) {
251*d5c9a868SElliott Hughes 				FREE(&RootDir);
252*d5c9a868SElliott Hughes 				exit(1);
253*d5c9a868SElliott Hughes 			}
254*d5c9a868SElliott Hughes 			longname[0] = '\0';
255*d5c9a868SElliott Hughes 		}
256*d5c9a868SElliott Hughes 		if(longname[0])
257*d5c9a868SElliott Hughes 			longname[strlen(newLabel)-1] = '\0';
258*d5c9a868SElliott Hughes 	}
259*d5c9a868SElliott Hughes 
260*d5c9a868SElliott Hughes 	if(strlen(newLabel) > 11) {
261*d5c9a868SElliott Hughes 		fprintf(stderr,"New label too long\n");
262*d5c9a868SElliott Hughes 		FREE(&RootDir);
263*d5c9a868SElliott Hughes 		exit(1);
264*d5c9a868SElliott Hughes 	}
265*d5c9a868SElliott Hughes 
266*d5c9a868SElliott Hughes 	if((!show || newLabel[0]) && !isNotFound(&entry)){
267*d5c9a868SElliott Hughes 		/* if we have a label, wipe it out before putting new one */
268*d5c9a868SElliott Hughes 		if(interactive && newLabel[0] == '\0')
269*d5c9a868SElliott Hughes 			if(ask_confirmation("Delete volume label (y/n): ")){
270*d5c9a868SElliott Hughes 				FREE(&RootDir);
271*d5c9a868SElliott Hughes 				exit(0);
272*d5c9a868SElliott Hughes 			}
273*d5c9a868SElliott Hughes 		entry.dir.attr = 0; /* for old mlabel */
274*d5c9a868SElliott Hughes 		wipeEntry(&entry);
275*d5c9a868SElliott Hughes 	}
276*d5c9a868SElliott Hughes 
277*d5c9a868SElliott Hughes 	if (newLabel[0] != '\0') {
278*d5c9a868SElliott Hughes 		ch.ignore_entry = 1;
279*d5c9a868SElliott Hughes 		result = mwrite_one(RootDir,newLabel,0,labelit,NULL,&ch) ?
280*d5c9a868SElliott Hughes 		  0 : 1;
281*d5c9a868SElliott Hughes 	}
282*d5c9a868SElliott Hughes 
283*d5c9a868SElliott Hughes 	have_boot = 0;
284*d5c9a868SElliott Hughes 	if( (!show || newLabel[0]) || set_serial != SER_NONE) {
285*d5c9a868SElliott Hughes 		Fs = GetFs(RootDir);
286*d5c9a868SElliott Hughes 		have_boot = (force_pread(Fs,boot.characters,0,sizeof(boot)) ==
287*d5c9a868SElliott Hughes 			     sizeof(boot));
288*d5c9a868SElliott Hughes 	}
289*d5c9a868SElliott Hughes 
290*d5c9a868SElliott Hughes 	if(WORD_S(fatlen)) {
291*d5c9a868SElliott Hughes 	    labelBlock = &boot.boot.ext.old.labelBlock;
292*d5c9a868SElliott Hughes 	} else {
293*d5c9a868SElliott Hughes 	    labelBlock = &boot.boot.ext.fat32.labelBlock;
294*d5c9a868SElliott Hughes 	}
295*d5c9a868SElliott Hughes 
296*d5c9a868SElliott Hughes 	if(!show || newLabel[0]){
297*d5c9a868SElliott Hughes 		dos_name_t dosname;
298*d5c9a868SElliott Hughes 		const char *shrtLabel;
299*d5c9a868SElliott Hughes 		doscp_t *cp;
300*d5c9a868SElliott Hughes 		if(!newLabel[0])
301*d5c9a868SElliott Hughes 			shrtLabel = "NO NAME    ";
302*d5c9a868SElliott Hughes 		else
303*d5c9a868SElliott Hughes 			shrtLabel = newLabel;
304*d5c9a868SElliott Hughes 		cp = GET_DOSCONVERT(Fs);
305*d5c9a868SElliott Hughes 		label_name_pc(cp, shrtLabel, verbose, &mangled, &dosname);
306*d5c9a868SElliott Hughes 
307*d5c9a868SElliott Hughes 		if(have_boot && boot.boot.descr >= 0xf0 && has_BPB4) {
308*d5c9a868SElliott Hughes 			strncpy(labelBlock->label, dosname.base, 8);
309*d5c9a868SElliott Hughes 			strncpy(labelBlock->label+8, dosname.ext, 3);
310*d5c9a868SElliott Hughes 			need_write_boot = 1;
311*d5c9a868SElliott Hughes 
312*d5c9a868SElliott Hughes 		}
313*d5c9a868SElliott Hughes 	}
314*d5c9a868SElliott Hughes 
315*d5c9a868SElliott Hughes 	if((set_serial != SER_NONE) & have_boot) {
316*d5c9a868SElliott Hughes 		if(have_boot && boot.boot.descr >= 0xf0 && has_BPB4) {
317*d5c9a868SElliott Hughes 			set_dword(labelBlock->serial, serial);
318*d5c9a868SElliott Hughes 			need_write_boot = 1;
319*d5c9a868SElliott Hughes 		}
320*d5c9a868SElliott Hughes 	}
321*d5c9a868SElliott Hughes 
322*d5c9a868SElliott Hughes 	if(need_write_boot) {
323*d5c9a868SElliott Hughes 		force_pwrite(Fs, (char *)&boot, 0, sizeof(boot));
324*d5c9a868SElliott Hughes 		/* If this is fat 32, write backup boot sector too */
325*d5c9a868SElliott Hughes 		if(!WORD_S(fatlen)) {
326*d5c9a868SElliott Hughes 			int backupBoot = WORD_S(ext.fat32.backupBoot);
327*d5c9a868SElliott Hughes 			force_pwrite(Fs, (char *)&boot,
328*d5c9a868SElliott Hughes 				     backupBoot * WORD_S(secsiz),
329*d5c9a868SElliott Hughes 				     sizeof(boot));
330*d5c9a868SElliott Hughes 		}
331*d5c9a868SElliott Hughes 	}
332*d5c9a868SElliott Hughes 
333*d5c9a868SElliott Hughes 	FREE(&RootDir);
334*d5c9a868SElliott Hughes 	exit(result);
335*d5c9a868SElliott Hughes }
336