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