blob: c90c72246de19d9181ed2549acc24281be46a813 [file] [log] [blame]
/* Copyright 1986-1992 Emmet P. Gray.
* Copyright 1996-1998,2000-2002,2005,2007-2009 Alain Knaff.
* This file is part of mtools.
*
* Mtools is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Mtools is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Mtools. If not, see <http://www.gnu.org/licenses/>.
*
* mlabel.c
* Make an MSDOS volume label
*/
#include "sysincludes.h"
#include "msdos.h"
#include "mainloop.h"
#include "vfat.h"
#include "mtools.h"
#include "nameclash.h"
#include "file_name.h"
static void _label_name(doscp_t *cp, const char *filename, int verbose UNUSEDP,
int *mangled, dos_name_t *ans, int preserve_case)
{
int len;
int i;
int have_lower, have_upper;
wchar_t wbuffer[12];
memset(ans, ' ', sizeof(*ans)-1);
ans->sentinel = '\0';
len = native_to_wchar(filename, wbuffer, 11, 0, 0);
if(len > 11){
*mangled = 1;
len = 11;
} else
*mangled = 0;
have_lower = have_upper = 0;
for(i=0; i<len; i++){
if(islower(wbuffer[i]))
have_lower = 1;
if(isupper(wbuffer[i]))
have_upper = 1;
if(!preserve_case)
wbuffer[i] = ch_towupper(wbuffer[i]);
if(
#ifdef HAVE_WCHAR_H
wcschr(L"^+=/[]:,?*\\<>|\".", wbuffer[i])
#else
strchr("^+=/[]:,?*\\<>|\".", wbuffer[i])
#endif
){
*mangled = 1;
wbuffer[i] = '~';
}
}
if (have_lower && have_upper)
*mangled = 1;
wchar_to_dos(cp, wbuffer, ans->base, len, mangled);
}
void label_name_uc(doscp_t *cp, const char *filename, int verbose,
int *mangled, dos_name_t *ans)
{
_label_name(cp, filename, verbose, mangled, ans, 0);
}
void label_name_pc(doscp_t *cp, const char *filename, int verbose,
int *mangled, dos_name_t *ans)
{
_label_name(cp, filename, verbose, mangled, ans, 1);
}
int labelit(struct dos_name_t *dosname,
char *longname UNUSEDP,
void *arg0 UNUSEDP,
direntry_t *entry)
{
time_t now;
/* find out current time */
getTimeNow(&now);
mk_entry(dosname, 0x8, 0, 0, now, &entry->dir);
return 0;
}
static void usage(int ret) NORETURN;
static void usage(int ret)
{
fprintf(stderr, "Mtools version %s, dated %s\n",
mversion, mdate);
fprintf(stderr, "Usage: %s [-vscVn] [-N serial] drive:\n", progname);
exit(ret);
}
void mlabel(int argc, char **argv, int type UNUSEDP) NORETURN;
void mlabel(int argc, char **argv, int type UNUSEDP)
{
const char *newLabel="";
int verbose, clear, interactive, show;
direntry_t entry;
int result=0;
char longname[VBUFSIZE];
char shortname[45];
ClashHandling_t ch;
struct MainParam_t mp;
Stream_t *RootDir;
int c;
int mangled;
enum { SER_NONE, SER_RANDOM, SER_SET } set_serial = SER_NONE;
uint32_t serial = 0;
int need_write_boot = 0;
int have_boot = 0;
char *eptr;
union bootsector boot;
Stream_t *Fs=0;
int r;
struct label_blk_t *labelBlock;
int isRo=0;
int *isRop=NULL;
char drive;
init_clash_handling(&ch);
ch.name_converter = label_name_uc;
ch.ignore_entry = -2;
ch.is_label = 1;
verbose = 0;
clear = 0;
show = 0;
if(helpFlag(argc, argv))
usage(0);
while ((c = getopt(argc, argv, "i:vcsnN:h")) != EOF) {
switch (c) {
case 'i':
set_cmd_line_image(optarg);
break;
case 'v':
verbose = 1;
break;
case 'c':
clear = 1;
break;
case 's':
show = 1;
break;
case 'n':
set_serial = SER_RANDOM;
init_random();
serial=random();
break;
case 'N':
set_serial = SER_SET;
errno=0;
serial = strtou32(optarg, &eptr, 16);
if(*eptr) {
fprintf(stderr,
"%s not a valid serial number\n",
optarg);
exit(1);
}
check_number_parse_errno(c, optarg, eptr);
break;
case 'h':
usage(0);
default:
usage(1);
}
}
if (argc - optind > 1)
usage(1);
if(argc - optind == 1) {
if(!argv[optind][0] || argv[optind][1] != ':')
usage(1);
drive = ch_toupper(argv[argc -1][0]);
newLabel = argv[optind]+2;
} else {
drive = get_default_drive();
}
init_mp(&mp);
if(strlen(newLabel) > VBUFSIZE) {
fprintf(stderr, "Label too long\n");
FREE(&RootDir);
exit(1);
}
interactive = !show && !clear &&!newLabel[0] &&
(set_serial == SER_NONE);
if(!clear && !newLabel[0]) {
isRop = &isRo;
}
if(clear && newLabel[0]) {
/* Clear and new label specified both */
fprintf(stderr, "Both clear and new label specified\n");
FREE(&RootDir);
exit(1);
}
RootDir = open_root_dir(drive, isRop ? 0 : O_RDWR, isRop);
if(isRo) {
show = 1;
interactive = 0;
}
if(!RootDir) {
fprintf(stderr, "%s: Cannot initialize drive\n", argv[0]);
exit(1);
}
initializeDirentry(&entry, RootDir);
r=vfat_lookup(&entry, 0, 0, ACCEPT_LABEL | MATCH_ANY,
shortname, sizeof(shortname),
longname, sizeof(longname));
if (r == -2) {
FREE(&RootDir);
exit(1);
}
if(show || interactive){
if(isNotFound(&entry))
printf(" Volume has no label\n");
else if (*longname)
printf(" Volume label is %s (abbr=%s)\n",
longname, shortname);
else
printf(" Volume label is %s\n", shortname);
}
/* ask for new label */
if(interactive){
saved_sig_state ss;
newLabel = longname;
allow_interrupts(&ss);
fprintf(stderr,"Enter the new volume label : ");
if(fgets(longname, VBUFSIZE, stdin) == NULL) {
fprintf(stderr, "\n");
if(errno == EINTR) {
FREE(&RootDir);
exit(1);
}
longname[0] = '\0';
}
if(longname[0])
longname[strlen(newLabel)-1] = '\0';
}
if(strlen(newLabel) > 11) {
fprintf(stderr,"New label too long\n");
FREE(&RootDir);
exit(1);
}
if((!show || newLabel[0]) && !isNotFound(&entry)){
/* if we have a label, wipe it out before putting new one */
if(interactive && newLabel[0] == '\0')
if(ask_confirmation("Delete volume label (y/n): ")){
FREE(&RootDir);
exit(0);
}
entry.dir.attr = 0; /* for old mlabel */
wipeEntry(&entry);
}
if (newLabel[0] != '\0') {
ch.ignore_entry = 1;
result = mwrite_one(RootDir,newLabel,0,labelit,NULL,&ch) ?
0 : 1;
}
have_boot = 0;
if( (!show || newLabel[0]) || set_serial != SER_NONE) {
Fs = GetFs(RootDir);
have_boot = (force_read(Fs,boot.characters,0,sizeof(boot)) ==
sizeof(boot));
}
if(WORD_S(fatlen)) {
labelBlock = &boot.boot.ext.old.labelBlock;
} else {
labelBlock = &boot.boot.ext.fat32.labelBlock;
}
if(!show || newLabel[0]){
dos_name_t dosname;
const char *shrtLabel;
doscp_t *cp;
if(!newLabel[0])
shrtLabel = "NO NAME ";
else
shrtLabel = newLabel;
cp = GET_DOSCONVERT(Fs);
label_name_pc(cp, shrtLabel, verbose, &mangled, &dosname);
if(have_boot && boot.boot.descr >= 0xf0 && has_BPB4) {
strncpy(labelBlock->label, dosname.base, 8);
strncpy(labelBlock->label+8, dosname.ext, 3);
need_write_boot = 1;
}
}
if((set_serial != SER_NONE) & have_boot) {
if(have_boot && boot.boot.descr >= 0xf0 && has_BPB4) {
set_dword(labelBlock->serial, serial);
need_write_boot = 1;
}
}
if(need_write_boot) {
force_write(Fs, (char *)&boot, 0, sizeof(boot));
/* If this is fat 32, write backup boot sector too */
if(!WORD_S(fatlen)) {
int backupBoot = WORD_S(ext.fat32.backupBoot);
force_write(Fs, (char *)&boot,
backupBoot * WORD_S(secsiz),
sizeof(boot));
}
}
FREE(&RootDir);
exit(result);
}