diff --git a/libntfs/volume.c b/libntfs/volume.c index 8b1bdffc..701b224e 100644 --- a/libntfs/volume.c +++ b/libntfs/volume.c @@ -764,7 +764,7 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long rwflag) Dperror("Failed to open inode"); goto error_exit; } - /* Get an ntfs attribute for $UpCase/$DATA. */ + /* Get a search context for the $Volume/$VOLUME_INFORMATION lookup. */ ctx = ntfs_attr_get_search_ctx(ni, NULL); if (!ctx) { Dputs(FAILED); @@ -807,46 +807,70 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long rwflag) /* Do not use le16_to_cpu() macro here as our VOLUME_FLAGS are defined using cpu_to_le16() macro and hence are consistent. */ vol->flags = vinf->flags; - /* Find the $VOLUME_NAME attribute. */ + /* + * Reinitialize the search context for the $Volume/$VOLUME_NAME lookup. + */ ntfs_attr_reinit_search_ctx(ctx); if (ntfs_attr_lookup(AT_VOLUME_NAME, AT_UNNAMED, 0, 0, 0, NULL, 0, ctx)) { - Dputs(FAILED); - Dputs("$VOLUME_NAME attribute not found in $Volume?!?"); - goto error_exit; - } - a = ctx->attr; - /* Has to be resident. */ - if (a->non_resident) { - Dputs(FAILED); - Dputs("Error: Attribute $VOLUME_NAME must be resident!"); - errno = EIO; - goto error_exit; - } - /* Get a pointer to the value of the attribute. */ - vname = (uchar_t*)(le16_to_cpu(a->value_offset) + (char*)a); - u = le32_to_cpu(a->value_length) / 2; - /* Convert Unicode volume name to current locale multibyte format. */ - vol->vol_name = NULL; - if (ntfs_ucstombs(vname, u, &vol->vol_name, 0) == -1) { - Dperror("Error: Volume name could not be converted to " - "current locale"); - Dputs("Forcing name into ASCII by replacing non-ASCII " - "characters with underscores."); - vol->vol_name = malloc(u + 1); + if (errno != ENOENT) { + Dputs(FAILED); + Dputs("Error: Lookup of $VOLUME_NAME attribute in " + "$Volume failed. This probably means " + "something is corrupt. Run chkdsk."); + goto error_exit; + } + /* + * Attribute not present. This has been seen in the field. + * Treat this the same way as if the attribute was present but + * had zero length. + */ + vol->vol_name = malloc(1); if (!vol->vol_name) { Dputs(FAILED); Dputs("Error: Unable to allocate memory for volume " "name!"); goto error_exit; } - for (j = 0; j < (s32)u; j++) { - uchar_t uc = le16_to_cpu(vname[j]); - if (uc > 0xff) - uc = (uchar_t)'_'; - vol->vol_name[j] = (char)uc; + vol->vol_name[0] = '\0'; + } else { + a = ctx->attr; + /* Has to be resident. */ + if (a->non_resident) { + Dputs(FAILED); + Dputs("Error: Attribute $VOLUME_NAME must be " + "resident!"); + errno = EIO; + goto error_exit; + } + /* Get a pointer to the value of the attribute. */ + vname = (uchar_t*)(le16_to_cpu(a->value_offset) + (char*)a); + u = le32_to_cpu(a->value_length) / 2; + /* + * Convert Unicode volume name to current locale multibyte + * format. + */ + vol->vol_name = NULL; + if (ntfs_ucstombs(vname, u, &vol->vol_name, 0) == -1) { + Dperror("Error: Volume name could not be converted to " + "current locale"); + Dputs("Forcing name into ASCII by replacing non-ASCII " + "characters with underscores."); + vol->vol_name = malloc(u + 1); + if (!vol->vol_name) { + Dputs(FAILED); + Dputs("Error: Unable to allocate memory for " + "volume name!"); + goto error_exit; + } + for (j = 0; j < (s32)u; j++) { + uchar_t uc = le16_to_cpu(vname[j]); + if (uc > 0xff) + uc = (uchar_t)'_'; + vol->vol_name[j] = (char)uc; + } + vol->vol_name[u] = '\0'; } - vol->vol_name[u] = '\0'; } Dputs(OK); ntfs_attr_put_search_ctx(ctx); diff --git a/ntfsprogs/ntfsinfo.c b/ntfsprogs/ntfsinfo.c index 399bc1c5..486089a0 100644 --- a/ntfsprogs/ntfsinfo.c +++ b/ntfsprogs/ntfsinfo.c @@ -517,29 +517,28 @@ void ntfs_dump_object_id_attr(ntfs_inode *inode) */ void ntfs_dump_volume_name_attr(ntfs_inode *inode) { - VOLUME_NAME *vol_name = NULL; - ATTR_RECORD *attr = NULL; - ntfs_attr_search_ctx *ctx = NULL; + VOLUME_NAME *vol_name = NULL; + ATTR_RECORD *attr = NULL; + ntfs_attr_search_ctx *ctx = NULL; - ctx = ntfs_attr_get_search_ctx(inode, NULL); - - if(ntfs_attr_lookup(AT_VOLUME_NAME, AT_UNNAMED, 0, 0, 0, NULL, 0, ctx)) { - if (errno != ENOENT) - fprintf(stderr, "ntfsinfo error: cannot look up attribute AT_VOLUME_NAME: %s\n", - strerror(errno)); + ctx = ntfs_attr_get_search_ctx(inode, NULL); + if (ntfs_attr_lookup(AT_VOLUME_NAME, AT_UNNAMED, 0, 0, 0, NULL, 0, + ctx)) { + if (errno != ENOENT) + fprintf(stderr, "ntfsinfo error: cannot look up " + "attribute AT_VOLUME_NAME: %s\n", + strerror(errno)); + ntfs_attr_put_search_ctx(ctx); + return; + } + attr = ctx->attr; + vol_name = (VOLUME_NAME*)((char *)attr + + le16_to_cpu(attr->value_offset)); + printf("Dumping $VOLUME_NAME (0x60)\n"); + // FIXME: convert the name to current locale multibyte sequence + // then output the converted name. + //printf("\tVolume Name: \t\t\t %s\n", vol_name->name); ntfs_attr_put_search_ctx(ctx); - return; - } - - attr = ctx->attr; - - vol_name = (VOLUME_NAME*)((char *)attr + le16_to_cpu(attr->value_offset)); - - printf("Dumping $VOLUME_NAME (0x60)\n"); - - //printf("\tVolume Name: \t\t\t %s\n", vol_name->name); - - ntfs_attr_put_search_ctx(ctx); } diff --git a/ntfsprogs/ntfslabel.c b/ntfsprogs/ntfslabel.c index 8ea8843a..74597805 100644 --- a/ntfsprogs/ntfslabel.c +++ b/ntfsprogs/ntfslabel.c @@ -2,7 +2,7 @@ * ntfslabel - Part of the Linux-NTFS project. * * Copyright (c) 2002 Matthew J. Fanto - * Copyright (c) 2002 Anton Altaparmakov + * Copyright (c) 2002-2004 Anton Altaparmakov * Copyright (c) 2002-2003 Richard Russon * * This utility will display/change the label on an NTFS partition. @@ -64,7 +64,7 @@ void version (void) EXEC_NAME, VERSION); printf ("Copyright (c)\n"); printf (" 2002 Matthew J. Fanto\n"); - printf (" 2002 Anton Altaparmakov\n"); + printf (" 2002-2004 Anton Altaparmakov\n"); printf (" 2002-2003 Richard Russon\n"); printf ("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home); } @@ -164,7 +164,8 @@ int parse_options (int argc, char *argv[]) } if (opts.quiet && opts.verbose) { - Eprintf ("You may not use --quiet and --verbose at the same time.\n"); + Eprintf ("You may not use --quiet and --verbose at " + "the same time.\n"); err++; } } @@ -283,15 +284,21 @@ int change_label(ntfs_volume *vol, unsigned long mnt_flags, char *label, BOOL fo perror("Failed to get attribute search context"); goto err_out; } - if (ntfs_attr_lookup(AT_VOLUME_NAME, AT_UNNAMED, 0, 0, 0, NULL, 0, ctx)) { - perror("Lookup of $VOLUME_NAME attribute failed"); - goto err_out; - } - a = ctx->attr; - if (a->non_resident) { - fprintf(stderr, "Error: Attribute $VOLUME_NAME must be " - "resident.\n"); - goto err_out; + if (ntfs_attr_lookup(AT_VOLUME_NAME, AT_UNNAMED, 0, 0, 0, NULL, 0, + ctx)) { + if (errno != ENOENT) { + perror("Lookup of $VOLUME_NAME attribute failed"); + goto err_out; + } + /* The volume name attribute does not exist. Need to add it. */ + a = NULL; + } else { + a = ctx->attr; + if (a->non_resident) { + fprintf(stderr, "Error: Attribute $VOLUME_NAME must be " + "resident.\n"); + goto err_out; + } } label_len = ntfs_mbstoucs(label, &new_label, 0); if (label_len == -1) { @@ -306,11 +313,38 @@ int change_label(ntfs_volume *vol, unsigned long mnt_flags, char *label, BOOL fo label_len = 0x100; new_label[label_len / sizeof(uchar_t)] = cpu_to_le16(L'\0'); } - if (resize_resident_attribute_value(mrec, a, label_len)) { - perror("Error resizing resident attribute"); - goto err_out; + if (a) { + if (resize_resident_attribute_value(mrec, a, label_len)) { + perror("Error resizing resident attribute"); + goto err_out; + } + } else { + /* sizeof(resident attribute record header) == 24 */ + int asize = (24 + label_len + 7) & ~7; + u32 biu = le32_to_cpu(mrec->bytes_in_use); + if (biu + asize > le32_to_cpu(mrec->bytes_allocated)) { + errno = ENOSPC; + perror("Error adding resident attribute"); + goto err_out; + } + a = ctx->attr; + memmove((u8*)a + asize, a, biu - ((u8*)a - (u8*)mrec)); + mrec->bytes_in_use = cpu_to_le32(biu + asize); + a->type = AT_VOLUME_NAME; + a->length = cpu_to_le32(asize); + a->non_resident = 0; + a->name_length = 0; + a->name_offset = cpu_to_le16(24); + a->flags = cpu_to_le16(0); + a->instance = mrec->next_attr_instance; + mrec->next_attr_instance = cpu_to_le16((le16_to_cpu( + mrec->next_attr_instance) + 1) & 0xffff); + a->value_length = cpu_to_le32(label_len); + a->value_offset = a->name_offset; + a->resident_flags = 0; + a->reservedR = 0; } - memcpy((char*)a + le16_to_cpu(a->value_offset), new_label, label_len); + memcpy((u8*)a + le16_to_cpu(a->value_offset), new_label, label_len); if (ntfs_mft_record_write(vol, (MFT_REF)FILE_Volume, mrec)) { perror("Error writing MFT Record to disk"); goto err_out;