mirror of
https://git.code.sf.net/p/ntfs-3g/ntfs-3g.git
synced 2024-11-27 20:14:10 +08:00
7506d8b80b
The MS_* flags originated from system constants. However the flags passed to ntfs_mount were really unrelated to the system constants and many new MS_* flags had to be introduced as different features were added to the library. Those flags had no counterparts in any system APIs, so using the same naming scheme is inappropriate. Instead, let's namespace these flags similarly to what has already been done in ntfsprogs/libntfs earlier. This avoids any possible conflicts with system constants. The values of the flags themselves are kept the same as earlier, so backward compatibility is retained.
459 lines
11 KiB
C
459 lines
11 KiB
C
/**
|
|
* ntfslabel - Part of the Linux-NTFS project.
|
|
*
|
|
* Copyright (c) 2002 Matthew J. Fanto
|
|
* Copyright (c) 2002-2005 Anton Altaparmakov
|
|
* Copyright (c) 2002-2003 Richard Russon
|
|
* Copyright (c) 2012 Jean-Pierre Andre
|
|
*
|
|
* This utility will display/change the label on an NTFS partition.
|
|
*
|
|
* This program 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 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program 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 this program (in the main directory of the Linux-NTFS
|
|
* distribution in the file COPYING); if not, write to the Free Software
|
|
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#ifdef HAVE_STDLIB_H
|
|
#include <stdlib.h>
|
|
#endif
|
|
#ifdef HAVE_STDIO_H
|
|
#include <stdio.h>
|
|
#endif
|
|
#ifdef HAVE_STRING_H
|
|
#include <string.h>
|
|
#endif
|
|
#ifdef HAVE_ERRNO_H
|
|
#include <errno.h>
|
|
#endif
|
|
#ifdef HAVE_LOCALE_H
|
|
#include <locale.h>
|
|
#endif
|
|
#ifdef HAVE_GETOPT_H
|
|
#include <getopt.h>
|
|
#endif
|
|
#ifdef HAVE_UNISTD_H
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#include "debug.h"
|
|
#include "mft.h"
|
|
#include "utils.h"
|
|
/* #include "version.h" */
|
|
#include "logging.h"
|
|
#include "misc.h"
|
|
|
|
static const char *EXEC_NAME = "ntfslabel";
|
|
|
|
static struct options {
|
|
char *device; /* Device/File to work with */
|
|
char *label; /* Set the label to this */
|
|
int quiet; /* Less output */
|
|
int verbose; /* Extra output */
|
|
int force; /* Override common sense */
|
|
int new_serial; /* Change the serial number */
|
|
long long serial; /* Forced serial number value */
|
|
int noaction; /* Do not write to disk */
|
|
} opts;
|
|
|
|
/**
|
|
* version - Print version information about the program
|
|
*
|
|
* Print a copyright statement and a brief description of the program.
|
|
*
|
|
* Return: none
|
|
*/
|
|
static void version(void)
|
|
{
|
|
ntfs_log_info("\n%s v%s (libntfs-3g) - Display, or set, the label for an "
|
|
"NTFS Volume.\n\n", EXEC_NAME, VERSION);
|
|
ntfs_log_info("Copyright (c)\n");
|
|
ntfs_log_info(" 2002 Matthew J. Fanto\n");
|
|
ntfs_log_info(" 2002-2005 Anton Altaparmakov\n");
|
|
ntfs_log_info(" 2002-2003 Richard Russon\n");
|
|
ntfs_log_info(" 2012 Jean-Pierre Andre\n");
|
|
ntfs_log_info("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home);
|
|
}
|
|
|
|
/**
|
|
* usage - Print a list of the parameters to the program
|
|
*
|
|
* Print a list of the parameters and options for the program.
|
|
*
|
|
* Return: none
|
|
*/
|
|
static void usage(void)
|
|
{
|
|
ntfs_log_info("\nUsage: %s [options] device [label]\n"
|
|
" -n, --no-action Do not write to disk\n"
|
|
" -f, --force Use less caution\n"
|
|
" --new-serial Set a new serial number\n"
|
|
" --new-half-serial Set a partial new serial number\n"
|
|
" -q, --quiet Less output\n"
|
|
" -v, --verbose More output\n"
|
|
" -V, --version Display version information\n"
|
|
" -h, --help Display this help\n\n",
|
|
EXEC_NAME);
|
|
ntfs_log_info("%s%s\n", ntfs_bugs, ntfs_home);
|
|
}
|
|
|
|
/**
|
|
* parse_options - Read and validate the programs command line
|
|
*
|
|
* Read the command line, verify the syntax and parse the options.
|
|
* This function is very long, but quite simple.
|
|
*
|
|
* Return: 1 Success
|
|
* 0 Error, one or more problems
|
|
*/
|
|
static int parse_options(int argc, char *argv[])
|
|
{
|
|
static const char *sopt = "-fh?IinqvV";
|
|
static const struct option lopt[] = {
|
|
{ "force", no_argument, NULL, 'f' },
|
|
{ "help", no_argument, NULL, 'h' },
|
|
{ "new-serial", optional_argument, NULL, 'I' },
|
|
{ "new-half-serial", optional_argument, NULL, 'i' },
|
|
{ "no-action", no_argument, NULL, 'n' },
|
|
{ "quiet", no_argument, NULL, 'q' },
|
|
{ "verbose", no_argument, NULL, 'v' },
|
|
{ "version", no_argument, NULL, 'V' },
|
|
{ NULL, 0, NULL, 0 },
|
|
};
|
|
|
|
int c = -1;
|
|
int err = 0;
|
|
int ver = 0;
|
|
int help = 0;
|
|
int levels = 0;
|
|
char *endserial;
|
|
|
|
opterr = 0; /* We'll handle the errors, thank you. */
|
|
|
|
while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) {
|
|
switch (c) {
|
|
case 1: /* A non-option argument */
|
|
if (!err && !opts.device)
|
|
opts.device = argv[optind-1];
|
|
else if (!err && !opts.label)
|
|
opts.label = argv[optind-1];
|
|
else
|
|
err++;
|
|
break;
|
|
case 'f':
|
|
opts.force++;
|
|
break;
|
|
case 'h':
|
|
case '?':
|
|
if (strncmp (argv[optind-1], "--log-", 6) == 0) {
|
|
if (!ntfs_log_parse_option (argv[optind-1]))
|
|
err++;
|
|
break;
|
|
}
|
|
help++;
|
|
break;
|
|
case 'I' : /* not proposed as a short option letter */
|
|
if (optarg) {
|
|
opts.serial = strtoll(optarg, &endserial, 16);
|
|
if (*endserial)
|
|
ntfs_log_error("Bad hexadecimal serial number.\n");
|
|
}
|
|
opts.new_serial |= 2;
|
|
break;
|
|
case 'i' : /* not proposed as a short option letter */
|
|
if (optarg) {
|
|
opts.serial = strtoll(optarg, &endserial, 16)
|
|
<< 32;
|
|
if (*endserial)
|
|
ntfs_log_error("Bad hexadecimal serial number.\n");
|
|
}
|
|
opts.new_serial |= 1;
|
|
break;
|
|
case 'n':
|
|
opts.noaction++;
|
|
break;
|
|
case 'q':
|
|
opts.quiet++;
|
|
ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET);
|
|
break;
|
|
case 'v':
|
|
opts.verbose++;
|
|
ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE);
|
|
break;
|
|
case 'V':
|
|
ver++;
|
|
break;
|
|
default:
|
|
ntfs_log_error("Unknown option '%s'.\n", argv[optind-1]);
|
|
err++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Make sure we're in sync with the log levels */
|
|
levels = ntfs_log_get_levels();
|
|
if (levels & NTFS_LOG_LEVEL_VERBOSE)
|
|
opts.verbose++;
|
|
if (!(levels & NTFS_LOG_LEVEL_QUIET))
|
|
opts.quiet++;
|
|
|
|
if (help || ver) {
|
|
opts.quiet = 0;
|
|
} else {
|
|
if (opts.device == NULL) {
|
|
if (argc > 1)
|
|
ntfs_log_error("You must specify a device.\n");
|
|
err++;
|
|
}
|
|
|
|
if (opts.quiet && opts.verbose) {
|
|
ntfs_log_error("You may not use --quiet and --verbose at "
|
|
"the same time.\n");
|
|
err++;
|
|
}
|
|
}
|
|
|
|
if (ver)
|
|
version();
|
|
if (help || err)
|
|
usage();
|
|
|
|
return (!err && !help && !ver);
|
|
}
|
|
|
|
static int change_serial(ntfs_volume *vol, u64 sector, le64 serial_number,
|
|
NTFS_BOOT_SECTOR *bs, NTFS_BOOT_SECTOR *oldbs)
|
|
{
|
|
int res;
|
|
le64 mask;
|
|
BOOL same;
|
|
|
|
res = -1;
|
|
if ((ntfs_pread(vol->dev, sector << vol->sector_size_bits,
|
|
vol->sector_size, bs) == vol->sector_size)) {
|
|
same = TRUE;
|
|
if (!sector)
|
|
/* save the real bootsector */
|
|
memcpy(oldbs, bs, vol->sector_size);
|
|
else
|
|
/* backup bootsector must be similar */
|
|
same = !memcmp(oldbs, bs, vol->sector_size);
|
|
if (same) {
|
|
if (opts.new_serial & 2)
|
|
bs->volume_serial_number = serial_number;
|
|
else {
|
|
mask = const_cpu_to_le64(~0x0ffffffffULL);
|
|
bs->volume_serial_number
|
|
= (serial_number & mask)
|
|
| (bs->volume_serial_number & ~mask);
|
|
}
|
|
if (opts.noaction
|
|
|| (ntfs_pwrite(vol->dev,
|
|
sector << vol->sector_size_bits,
|
|
vol->sector_size, bs) == vol->sector_size)) {
|
|
res = 0;
|
|
}
|
|
} else {
|
|
ntfs_log_info("* Warning : the backup boot sector"
|
|
" does not match (leaving unchanged)\n");
|
|
res = 0;
|
|
}
|
|
}
|
|
return (res);
|
|
}
|
|
|
|
static int set_new_serial(ntfs_volume *vol)
|
|
{
|
|
NTFS_BOOT_SECTOR *bs; /* full boot sectors */
|
|
NTFS_BOOT_SECTOR *oldbs; /* full original boot sector */
|
|
le64 serial_number;
|
|
u64 number_of_sectors;
|
|
u64 sn;
|
|
int res;
|
|
|
|
res = -1;
|
|
bs = (NTFS_BOOT_SECTOR*)ntfs_malloc(vol->sector_size);
|
|
oldbs = (NTFS_BOOT_SECTOR*)ntfs_malloc(vol->sector_size);
|
|
if (bs && oldbs) {
|
|
if (opts.serial)
|
|
serial_number = cpu_to_le64(opts.serial);
|
|
else {
|
|
/* different values for parallel processes */
|
|
srandom(time((time_t*)NULL) ^ (getpid() << 16));
|
|
sn = ((u64)random() << 32)
|
|
| ((u64)random() & 0xffffffff);
|
|
serial_number = cpu_to_le64(sn);
|
|
}
|
|
if (!change_serial(vol, 0, serial_number, bs, oldbs)) {
|
|
number_of_sectors = le64_to_cpu(bs->number_of_sectors);
|
|
if (!change_serial(vol, number_of_sectors,
|
|
serial_number, bs, oldbs)) {
|
|
ntfs_log_info("New serial number : %016llx\n",
|
|
(long long)le64_to_cpu(
|
|
bs->volume_serial_number));
|
|
res = 0;
|
|
}
|
|
}
|
|
free(bs);
|
|
free(oldbs);
|
|
}
|
|
if (res)
|
|
ntfs_log_info("Error setting a new serial number\n");
|
|
return (res);
|
|
}
|
|
|
|
static int print_serial(ntfs_volume *vol)
|
|
{
|
|
NTFS_BOOT_SECTOR *bs; /* full boot sectors */
|
|
int res;
|
|
|
|
res = -1;
|
|
bs = (NTFS_BOOT_SECTOR*)ntfs_malloc(vol->sector_size);
|
|
if (bs
|
|
&& (ntfs_pread(vol->dev, 0,
|
|
vol->sector_size, bs) == vol->sector_size)) {
|
|
ntfs_log_info("Serial number : %016llx\n",
|
|
(long long)le64_to_cpu(bs->volume_serial_number));
|
|
res = 0;
|
|
free(bs);
|
|
}
|
|
if (res)
|
|
ntfs_log_info("Error getting the serial number\n");
|
|
return (res);
|
|
}
|
|
|
|
/**
|
|
* print_label - display the current label of a mounted ntfs partition.
|
|
* @dev: device to read the label from
|
|
* @mnt_flags: mount flags of the device or 0 if not mounted
|
|
* @mnt_point: mount point of the device or NULL
|
|
*
|
|
* Print the label of the device @dev.
|
|
*/
|
|
static int print_label(ntfs_volume *vol, unsigned long mnt_flags)
|
|
{
|
|
int result = 0;
|
|
//XXX significant?
|
|
if ((mnt_flags & (NTFS_MF_MOUNTED | NTFS_MF_READONLY)) ==
|
|
NTFS_MF_MOUNTED) {
|
|
ntfs_log_error("%s is mounted read-write, results may be "
|
|
"unreliable.\n", opts.device);
|
|
result = 1;
|
|
}
|
|
|
|
if (opts.verbose)
|
|
ntfs_log_info("Volume label : %s\n", vol->vol_name);
|
|
else
|
|
ntfs_log_info("%s\n", vol->vol_name);
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* change_label - change the current label on a device
|
|
* @dev: device to change the label on
|
|
* @mnt_flags: mount flags of the device or 0 if not mounted
|
|
* @mnt_point: mount point of the device or NULL
|
|
* @label: the new label
|
|
*
|
|
* Change the label on the device @dev to @label.
|
|
*/
|
|
static int change_label(ntfs_volume *vol, char *label)
|
|
{
|
|
ntfschar *new_label = NULL;
|
|
int label_len;
|
|
int result = 0;
|
|
|
|
label_len = ntfs_mbstoucs(label, &new_label);
|
|
if (label_len == -1) {
|
|
ntfs_log_perror("Unable to convert label string to Unicode");
|
|
return 1;
|
|
}
|
|
else if (label_len*sizeof(ntfschar) > 0x100) {
|
|
ntfs_log_warning("New label is too long. Maximum %u characters "
|
|
"allowed. Truncating %u excess characters.\n",
|
|
(unsigned)(0x100 / sizeof(ntfschar)),
|
|
(unsigned)(label_len -
|
|
(0x100 / sizeof(ntfschar))));
|
|
label_len = 0x100 / sizeof(ntfschar);
|
|
label[label_len] = const_cpu_to_le16(0);
|
|
}
|
|
|
|
if(!opts.noaction)
|
|
result = ntfs_volume_rename(vol, new_label, label_len) ? 1 : 0;
|
|
|
|
free(new_label);
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* main - Begin here
|
|
*
|
|
* Start from here.
|
|
*
|
|
* Return: 0 Success, the program worked
|
|
* 1 Error, something went wrong
|
|
*/
|
|
int main(int argc, char **argv)
|
|
{
|
|
unsigned long mnt_flags = 0;
|
|
int result = 0;
|
|
ntfs_volume *vol;
|
|
|
|
ntfs_log_set_handler(ntfs_log_handler_outerr);
|
|
|
|
if (!parse_options(argc, argv))
|
|
return 1;
|
|
|
|
utils_set_locale();
|
|
|
|
if ((opts.label || opts.new_serial)
|
|
&& !opts.noaction
|
|
&& !opts.force
|
|
&& !ntfs_check_if_mounted(opts.device, &mnt_flags)
|
|
&& (mnt_flags & NTFS_MF_MOUNTED)) {
|
|
ntfs_log_error("Cannot make changes to a mounted device\n");
|
|
result = 1;
|
|
goto abort;
|
|
}
|
|
|
|
if (!opts.label && !opts.new_serial)
|
|
opts.noaction++;
|
|
|
|
vol = utils_mount_volume(opts.device,
|
|
(opts.noaction ? NTFS_MNT_RDONLY : 0) |
|
|
(opts.force ? NTFS_MNT_RECOVER : 0));
|
|
if (!vol)
|
|
return 1;
|
|
|
|
if (opts.new_serial) {
|
|
result = set_new_serial(vol);
|
|
if (result)
|
|
goto unmount;
|
|
} else {
|
|
if (opts.verbose)
|
|
result = print_serial(vol);
|
|
}
|
|
if (opts.label)
|
|
result = change_label(vol, opts.label);
|
|
else
|
|
result = print_label(vol, mnt_flags);
|
|
|
|
unmount :
|
|
ntfs_umount(vol, FALSE);
|
|
abort :
|
|
return result;
|
|
}
|
|
|