diff --git a/source/acls.c b/source/acls.c index 6328420..fe466c2 100644 --- a/source/acls.c +++ b/source/acls.c @@ -522,9 +522,7 @@ gid_t ntfs_find_group(const struct MAPPING* groupmapping, const SID * gsid) { gid_t gid; const struct MAPPING *p; - int gsidsz; - gsidsz = ntfs_sid_size(gsid); p = groupmapping; while (p && p->xid && !ntfs_same_sid(gsid, p->sid)) p = p->next; @@ -1901,7 +1899,6 @@ static int buildacls_posix(struct MAPPING* const mapping[], const SID *sid; int acecnt; int usidsz; - int gsidsz; int wsidsz; int asidsz; int ssidsz; @@ -1909,7 +1906,6 @@ static int buildacls_posix(struct MAPPING* const mapping[], le32 grants; usidsz = ntfs_sid_size(usid); - gsidsz = ntfs_sid_size(gsid); wsidsz = ntfs_sid_size(worldsid); asidsz = ntfs_sid_size(adminsid); ssidsz = ntfs_sid_size(systemsid); @@ -3132,7 +3128,6 @@ static int norm_ownadmin_permissions_posix(struct POSIX_SECURITY *posix_desc, u16 tag; u16 tagsset; struct POSIX_ACE *pxace; - int acccnt; mode_t denywrld; mode_t allow; mode_t deny; @@ -3141,7 +3136,6 @@ static int norm_ownadmin_permissions_posix(struct POSIX_SECURITY *posix_desc, mode = 0; pxace = posix_desc->acl.ace; - acccnt = posix_desc->acccnt; tagsset = 0; denywrld = 0; /* @@ -3881,12 +3875,10 @@ struct POSIX_SECURITY *ntfs_build_permissions_posix( int ntfs_build_permissions(const char *securattr, const SID *usid, const SID *gsid, BOOL isdir) { - const SECURITY_DESCRIPTOR_RELATIVE *phead; int perm; BOOL adminowns; BOOL groupowns; - phead = (const SECURITY_DESCRIPTOR_RELATIVE*)securattr; adminowns = ntfs_same_sid(usid,adminsid) || ntfs_same_sid(gsid,adminsid); groupowns = !adminowns && ntfs_same_sid(gsid,usid); @@ -3969,7 +3961,6 @@ static struct MAPLIST *getmappingitem(FILEREADER reader, void *fileid, { int src; int dst; - char *p; char *q; char *pu; char *pg; @@ -4003,7 +3994,6 @@ static struct MAPLIST *getmappingitem(FILEREADER reader, void *fileid, if (gotend) { pu = pg = (char*)NULL; /* decompose into uid, gid and sid */ - p = item->maptext; item->uidstr = item->maptext; item->gidstr = strchr(item->uidstr, ':'); if (item->gidstr) { diff --git a/source/attrib.c b/source/attrib.c index e6f614f..281a620 100644 --- a/source/attrib.c +++ b/source/attrib.c @@ -475,9 +475,10 @@ ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type, } cs = a->flags & (ATTR_IS_COMPRESSED | ATTR_IS_SPARSE); - + + /* a file may be sparse though its unnamed data is not (cf $UsnJrnl) */ if (na->type == AT_DATA && na->name == AT_UNNAMED && - ((!(a->flags & ATTR_IS_SPARSE) != !NAttrSparse(na)) || + (((a->flags & ATTR_IS_SPARSE) && !NAttrSparse(na)) || (!(a->flags & ATTR_IS_ENCRYPTED) != !NAttrEncrypted(na)))) { errno = EIO; ntfs_log_perror("Inode %lld has corrupt attribute flags " @@ -1774,7 +1775,6 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b) } need_to = { 0, 0 }; BOOL wasnonresident = FALSE; BOOL compressed; - BOOL sparse; BOOL updatemap; ntfs_log_enter("Entering for inode %lld, attr 0x%x, pos 0x%llx, count " @@ -1850,7 +1850,7 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b) goto errno_set; } #else - if (ntfs_attr_truncate(na, pos + count)) { + if (ntfs_attr_truncate_i(na, pos + count, HOLES_OK)) { ntfs_log_perror("Failed to enlarge attribute"); goto errno_set; } @@ -1860,7 +1860,6 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b) != const_cpu_to_le16(0); need_to.undo_data_size = 1; } - sparse = (na->data_flags & ATTR_IS_SPARSE) != const_cpu_to_le16(0); /* * For compressed data, a single full block was allocated * to deal with compression, possibly in a previous call. @@ -2221,7 +2220,7 @@ done: updatemap = (compressed ? NAttrFullyMapped(na) != 0 : update_from != -1); #endif - if (updatemap) + if (updatemap) { if (ntfs_attr_update_mapping_pairs(na, (update_from < 0 ? 0 : update_from))) { /* @@ -2231,6 +2230,10 @@ done: total = -1; goto out; } + if (!wasnonresident) + NAttrClearBeingNonResident(na); + NAttrClearDataAppending(na); + } out: ntfs_log_leave("\n"); return total; @@ -2292,7 +2295,8 @@ err_out: if (updatemap) ntfs_attr_update_mapping_pairs(na, 0); /* Restore original data_size if needed. */ - if (need_to.undo_data_size && ntfs_attr_truncate(na, old_data_size)) + if (need_to.undo_data_size + && ntfs_attr_truncate_i(na, old_data_size, HOLES_OK)) ntfs_log_perror("Failed to restore data_size"); errno = eo; errno_set: @@ -2310,7 +2314,6 @@ int ntfs_attr_pclose(ntfs_attr *na) ntfs_attr_search_ctx *ctx = NULL; runlist_element *rl; int eo; - s64 hole; int compressed_part; BOOL compressed; @@ -2422,7 +2425,6 @@ int ntfs_attr_pclose(ntfs_attr *na) goto rl_err_out; } if (rl->lcn < (LCN)0) { - hole = rl->vcn + rl->length; if (rl->lcn != (LCN)LCN_HOLE) { errno = EIO; ntfs_log_perror("%s: Unexpected LCN (%lld)", @@ -2475,6 +2477,7 @@ retry: goto out; } out: + NAttrClearComprClosing(na); ntfs_log_leave("\n"); return (!ok); rl_err_out: @@ -2531,6 +2534,7 @@ s64 ntfs_attr_mst_pread(ntfs_attr *na, const s64 pos, const s64 bk_cnt, { s64 br; u8 *end; + BOOL warn; ntfs_log_trace("Entering for inode 0x%llx, attr type 0x%x, pos 0x%llx.\n", (unsigned long long)na->ni->mft_no, na->type, @@ -2544,9 +2548,11 @@ s64 ntfs_attr_mst_pread(ntfs_attr *na, const s64 pos, const s64 bk_cnt, if (br <= 0) return br; br /= bk_size; + /* log errors unless silenced */ + warn = !na->ni || !na->ni->vol || !NVolNoFixupWarn(na->ni->vol); for (end = (u8*)dst + br * bk_size; (u8*)dst < end; dst = (u8*)dst + bk_size) - ntfs_mst_post_read_fixup((NTFS_RECORD*)dst, bk_size); + ntfs_mst_post_read_fixup_warn((NTFS_RECORD*)dst, bk_size, warn); /* Finally, return the number of blocks read. */ return br; } @@ -3544,6 +3550,14 @@ int ntfs_attr_size_bounds_check(const ntfs_volume *vol, const ATTR_TYPES type, min_size = sle64_to_cpu(ad->min_size); max_size = sle64_to_cpu(ad->max_size); + + /* The $AttrDef generated by Windows specifies 2 as min_size for the + * volume name attribute, but in reality Windows sets it to 0 when + * clearing the volume name. If we want to be able to clear the volume + * name we must also accept 0 as min_size, despite the $AttrDef + * definition. */ + if(type == AT_VOLUME_NAME) + min_size = 0; if ((min_size && (size < min_size)) || ((max_size > 0) && (size > max_size))) { @@ -4296,7 +4310,7 @@ add_non_resident: goto rm_attr_err_out; } /* Resize and set attribute value. */ - if (ntfs_attr_truncate(na, size) || + if (ntfs_attr_truncate_i(na, size, HOLES_OK) || (val && (ntfs_attr_pwrite(na, 0, size, val) != size))) { err = errno; ntfs_log_perror("Failed to initialize just added attribute"); @@ -4877,6 +4891,7 @@ cluster_free_err_out: ntfs_log_trace("Eeek! Failed to release allocated clusters in error " "code path. Leaving inconsistent metadata...\n"); NAttrClearNonResident(na); + NAttrClearFullyMapped(na); na->allocated_size = na->data_size; na->rl = NULL; free(rl); @@ -5000,7 +5015,7 @@ static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize, return (ret); } /* Resize non-resident attribute */ - return ntfs_attr_truncate(na, newsize); + return ntfs_attr_truncate_i(na, newsize, HOLES_OK); } else if (errno != ENOSPC && errno != EPERM) { err = errno; ntfs_log_perror("Failed to make attribute non-resident"); @@ -5348,7 +5363,6 @@ static int ntfs_attr_make_resident(ntfs_attr *na, ntfs_attr_search_ctx *ctx) * record is in a transiently corrupted state at this moment in time. */ if (ntfs_cluster_free(vol, na, 0, -1) < 0) { - err = errno; ntfs_log_perror("Eeek! Failed to release allocated clusters"); ntfs_log_trace("Ignoring error and leaving behind wasted " "clusters.\n"); @@ -5360,6 +5374,7 @@ static int ntfs_attr_make_resident(ntfs_attr *na, ntfs_attr_search_ctx *ctx) /* Update in-memory struct ntfs_attr. */ NAttrClearNonResident(na); + NAttrClearFullyMapped(na); NAttrClearSparse(na); NAttrClearEncrypted(na); na->initialized_size = na->data_size; @@ -5453,6 +5468,7 @@ static int ntfs_attr_update_meta(ATTR_RECORD *a, ntfs_attr *na, MFT_RECORD *m, NAttrClearSparse(na); a->flags &= ~ATTR_IS_SPARSE; + na->data_flags = a->flags; a->compression_unit = 0; memmove((u8*)a + le16_to_cpu(a->name_offset) - 8, @@ -6422,7 +6438,12 @@ out: int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize) { - return (ntfs_attr_truncate_i(na, newsize, HOLES_OK)); + int r; + + r = ntfs_attr_truncate_i(na, newsize, HOLES_OK); + NAttrClearDataAppending(na); + NAttrClearBeingNonResident(na); + return (r); } /* diff --git a/source/cache.c b/source/cache.c index 43ff151..2ad8d35 100644 --- a/source/cache.c +++ b/source/cache.c @@ -376,7 +376,6 @@ int ntfs_invalidate_cache(struct CACHE_HEADER *cache, int flags) { struct CACHED_GENERIC *current; - struct CACHED_GENERIC *previous; struct CACHED_GENERIC *next; struct HASH_ENTRY *link; int count; @@ -412,7 +411,6 @@ int ntfs_invalidate_cache(struct CACHE_HEADER *cache, * Search sequentially in LRU list */ current = cache->most_recent_entry; - previous = (struct CACHED_GENERIC*)NULL; while (current) { if (!compare(current, item)) { next = current->next; @@ -423,7 +421,6 @@ int ntfs_invalidate_cache(struct CACHE_HEADER *cache, current = next; count++; } else { - previous = current; current = current->next; } } diff --git a/source/compress.c b/source/compress.c index fe6bb66..aeb082d 100644 --- a/source/compress.c +++ b/source/compress.c @@ -81,259 +81,215 @@ typedef enum { NTFS_SB_IS_COMPRESSED = 0x8000, } ntfs_compression_constants; -#define THRESHOLD 3 /* minimal match length for compression */ -#define NIL NTFS_SB_SIZE /* End of tree's node */ - struct COMPRESS_CONTEXT { const unsigned char *inbuf; - unsigned int len; - unsigned int nbt; - int match_position; - unsigned int match_length; - u16 lson[NTFS_SB_SIZE + 1]; - u16 rson[NTFS_SB_SIZE + 257]; - u16 dad[NTFS_SB_SIZE + 1]; + int bufsize; + int size; + int rel; + int mxsz; + s16 head[256]; + s16 lson[NTFS_SB_SIZE]; + s16 rson[NTFS_SB_SIZE]; } ; /* - * Initialize the match tree - */ - -static void ntfs_init_compress_tree(struct COMPRESS_CONTEXT *pctx) -{ - int i; - - for (i = NTFS_SB_SIZE + 1; i <= NTFS_SB_SIZE + 256; i++) - pctx->rson[i] = NIL; /* root */ - for (i = 0; i < NTFS_SB_SIZE; i++) - pctx->dad[i] = NIL; /* node */ -} - -/* - * Insert a new node into match tree for quickly locating - * further similar strings - */ - -static void ntfs_new_node (struct COMPRESS_CONTEXT *pctx, - unsigned int r) -{ - unsigned int pp; - BOOL less; - BOOL done; - const unsigned char *key; - int c; - unsigned long mxi; - unsigned int mxl; - - mxl = (1 << (16 - pctx->nbt)) + 2; - less = FALSE; - done = FALSE; - key = &pctx->inbuf[r]; - pp = NTFS_SB_SIZE + 1 + key[0]; - pctx->rson[r] = pctx->lson[r] = NIL; - pctx->match_length = 0; - do { - if (!less) { - if (pctx->rson[pp] != NIL) - pp = pctx->rson[pp]; - else { - pctx->rson[pp] = r; - pctx->dad[r] = pp; - done = TRUE; - } - } else { - if (pctx->lson[pp] != NIL) - pp = pctx->lson[pp]; - else { - pctx->lson[pp] = r; - pctx->dad[r] = pp; - done = TRUE; - } - } - if (!done) { - register unsigned long i; - register const unsigned char *p1,*p2; - - i = 1; - mxi = NTFS_SB_SIZE - r; - if (mxi < 2) - less = FALSE; - else { - p1 = key; - p2 = &pctx->inbuf[pp]; - /* this loop has a significant impact on performances */ - do { - } while ((p1[i] == p2[i]) && (++i < mxi)); - less = (i < mxi) && (p1[i] < p2[i]); - } - if (i >= THRESHOLD) { - if (i > pctx->match_length) { - pctx->match_position = - r - pp + 2*NTFS_SB_SIZE - 1; - if ((pctx->match_length = i) > mxl) { - i = pctx->rson[pp]; - pctx->rson[r] = i; - pctx->dad[i] = r; - i = pctx->lson[pp]; - pctx->lson[r] = i; - pctx->dad[i] = r; - i = pctx->dad[pp]; - pctx->dad[r] = i; - if (pctx->rson[i] == pp) - pctx->rson[i] = r; - else - pctx->lson[i] = r; - /* remove pp */ - pctx->dad[pp] = NIL; - done = TRUE; - pctx->match_length = mxl; - } - } else - if ((i == pctx->match_length) - && ((c = (r - pp + 2*NTFS_SB_SIZE - 1)) - < pctx->match_position)) - pctx->match_position = c; - } - } - } while (!done); -} - -/* - * Search for the longest previous string matching the - * current one + * Search for the longest sequence matching current position * - * Returns the end of the longest current string which matched - * or zero if there was a bug + * A binary tree is maintained to locate all previously met sequences, + * and this function has to be called for all of them. + * + * This function is heavily used, it has to be optimized carefully + * + * Returns the size of the longest match, + * zero if no match is found. */ -static unsigned int ntfs_nextmatch(struct COMPRESS_CONTEXT *pctx, - unsigned int rr, int dd) +static int ntfs_best_match(struct COMPRESS_CONTEXT *pctx, int i) { - unsigned int bestlen = 0; + s16 *prev; + int node; + register long j; + long maxpos; + long startj; + long bestj; + int bufsize; + int bestnode; + register const unsigned char *p1,*p2; - do { - rr++; - if (pctx->match_length > 0) - pctx->match_length--; - if (!pctx->len) { - ntfs_log_error("compress bug : void run\n"); - goto bug; - } - if (--pctx->len) { - if (rr >= NTFS_SB_SIZE) { - ntfs_log_error("compress bug : buffer overflow\n"); - goto bug; - } - if (((rr + bestlen) < NTFS_SB_SIZE)) { - while ((unsigned int)(1 << pctx->nbt) - <= (rr - 1)) - pctx->nbt++; - ntfs_new_node(pctx,rr); - if (pctx->match_length > bestlen) - bestlen = pctx->match_length; - } else - if (dd > 0) { - rr += dd; - if ((int)pctx->match_length > dd) - pctx->match_length -= dd; - else - pctx->match_length = 0; - if ((int)pctx->len < dd) { - ntfs_log_error("compress bug : run overflows\n"); - goto bug; + p1 = pctx->inbuf; + node = pctx->head[p1[i] & 255]; + if (node >= 0) { + /* search the best match at current position */ + bestnode = node; + bufsize = pctx->bufsize; + /* restrict matches to the longest allowed sequence */ + maxpos = bufsize; + if ((i + pctx->mxsz) < maxpos) + maxpos = i + pctx->mxsz; + startj = i + 1 - maxpos; + bestj = startj; + /* make indexes relative to end of allowed position */ + p1 = &p1[maxpos]; + if (startj < 0) { + do { + /* indexes are negative */ + p2 = &p1[node - i]; + /* no need to compare the first byte */ + j = startj; + /* the second byte cannot lead to useful compression */ + if (p1[j] == p2[j]) { + j++; + if (j < 0) { + do { + } while ((p1[j] == p2[j]) + && (++j < 0)); + } + /* remember the match, if better */ + if (j > bestj) { + bestj = j; + bestnode = node; } - pctx->len -= dd; - dd = 0; } + /* walk in the tree in the right direction */ + if ((j < 0) && (p1[j] < p2[j])) + prev = &pctx->lson[node]; + else + prev = &pctx->rson[node]; + node = *prev; + /* stop if reaching a leaf or maximum length */ + } while ((node >= 0) && (j < 0)); + /* put the node into the tree if we reached a leaf */ + if (node < 0) + *prev = i; } - } while (dd-- > 0); - return (rr); -bug : - return (0); + /* done, return the best match */ + pctx->size = bestj + maxpos - i; + pctx->rel = bestnode - i; + } else { + pctx->head[p1[i] & 255] = i; + pctx->size = 0; + pctx->rel = 0; + } + return (pctx->size); } /* - * Compress an input block + * Compress a 4096-byte block * - * Returns the size of the compressed block (including header) - * or zero if there was an error + * Returns a header of two bytes followed by the compressed data. + * If compression is not effective, the header and an uncompressed + * block is returned. + * + * Note : two bytes may be output before output buffer overflow + * is detected, so a 4100-bytes output buffer must be reserved. + * + * Returns the size of the compressed block, including the + * header (minimal size is 2, maximum size is 4098) + * 0 if an error has been met. */ -static unsigned int ntfs_compress_block(const char *inbuf, - unsigned int size, char *outbuf) +static unsigned int ntfs_compress_block(const char *inbuf, int bufsize, + char *outbuf) { struct COMPRESS_CONTEXT *pctx; - char *ptag; - int dd; - unsigned int rr; - unsigned int last_match_length; - unsigned int q; + int i; /* current position */ + int j; /* end of best match from current position */ + int k; /* end of best match from next position */ + int offs; /* offset to best match */ + int n; + int bp; /* bits to store offset */ + int mxoff; /* max match offset : 1 << bp */ + int mxsz2; unsigned int xout; - unsigned int ntag; + unsigned int q; /* aggregated offset and size */ + int done; + char *ptag; /* location reserved for a tag */ + int tag; /* current value of tag */ + int ntag; /* count of bits still undefined in tag */ - pctx = (struct COMPRESS_CONTEXT*)malloc(sizeof(struct COMPRESS_CONTEXT)); + pctx = (struct COMPRESS_CONTEXT*)ntfs_malloc(sizeof(struct COMPRESS_CONTEXT)); if (pctx) { + for (n=0; nlson[n] = pctx->rson[n] = -1; + for (n=0; n<256; n++) + pctx->head[n] = -1; pctx->inbuf = (const unsigned char*)inbuf; - ntfs_init_compress_tree(pctx); + pctx->bufsize = bufsize; xout = 2; - ntag = 0; + n = 0; + i = 0; + bp = 4; + mxoff = 1 << bp; + pctx->mxsz = (1 << (16 - bp)) + 2; + tag = 0; + done = -1; + ntag = 8; ptag = &outbuf[xout++]; - *ptag = 0; - rr = 0; - pctx->nbt = 4; - pctx->len = size; - pctx->match_length = 0; - ntfs_new_node(pctx,0); - do { - if (pctx->match_length > pctx->len) - pctx->match_length = pctx->len; - if (pctx->match_length < THRESHOLD) { - pctx->match_length = 1; - if (ntag >= 8) { - ntag = 0; - ptag = &outbuf[xout++]; - *ptag = 0; + while ((i < bufsize) && (xout < (NTFS_SB_SIZE + 2))) { + /* adjust the longest match we can output */ + while (mxoff < i) { + bp++; + mxoff <<= 1; + pctx->mxsz = (pctx->mxsz + 2) >> 1; + } + /* search the best match at current position */ + if (done < i) + do { + ntfs_best_match(pctx,++done); + } while (done < i); + j = i + pctx->size; + if ((j - i) > pctx->mxsz) + j = i + pctx->mxsz; + + if ((j - i) > 2) { + offs = pctx->rel; + /* check whether there is a better run at i+1 */ + ntfs_best_match(pctx,i+1); + done = i+1; + k = i + 1 + pctx->size; + mxsz2 = pctx->mxsz; + if (mxoff <= i) + mxsz2 = (pctx->mxsz + 2) >> 1; + if ((k - i) > mxsz2) + k = i + mxsz2; + if (k > (j + 1)) { + /* issue a single byte */ + outbuf[xout++] = inbuf[i]; + i++; + } else { + q = (~offs << (16 - bp)) + + (j - i - 3); + outbuf[xout++] = q & 255; + outbuf[xout++] = (q >> 8) & 255; + tag |= (1 << (8 - ntag)); + i = j; } - outbuf[xout++] = inbuf[rr]; - ntag++; } else { - while ((unsigned int)(1 << pctx->nbt) - <= (rr - 1)) - pctx->nbt++; - q = (pctx->match_position << (16 - pctx->nbt)) - + pctx->match_length - THRESHOLD; - if (ntag >= 8) { - ntag = 0; - ptag = &outbuf[xout++]; - *ptag = 0; - } - *ptag |= 1 << ntag++; - outbuf[xout++] = q & 255; - outbuf[xout++] = (q >> 8) & 255; + outbuf[xout++] = inbuf[i]; + i++; } - last_match_length = pctx->match_length; - dd = last_match_length; - if (dd-- > 0) { - rr = ntfs_nextmatch(pctx,rr,dd); - if (!rr) - goto bug; + /* store the tag if fully used */ + if (!--ntag) { + *ptag = tag; + ntag = 8; + ptag = &outbuf[xout++]; + tag = 0; } - /* - * stop if input is exhausted or output has exceeded - * the maximum size. Two extra bytes have to be - * reserved in output buffer, as 3 bytes may be - * output in a loop. - */ - } while ((pctx->len > 0) - && (rr < size) && (xout < (NTFS_SB_SIZE + 2))); - /* uncompressed must be full size, so accept if better */ - if (xout < (NTFS_SB_SIZE + 2)) { + } + /* store the last tag, if partially used */ + if (ntag == 8) + xout--; + else + *ptag = tag; + /* uncompressed must be full size, accept if better */ + if ((i >= bufsize) && (xout < (NTFS_SB_SIZE + 2))) { outbuf[0] = (xout - 3) & 255; outbuf[1] = 0xb0 + (((xout - 3) >> 8) & 15); } else { - memcpy(&outbuf[2],inbuf,size); - if (size < NTFS_SB_SIZE) - memset(&outbuf[size+2],0,NTFS_SB_SIZE - size); + memcpy(&outbuf[2],inbuf,bufsize); + if (bufsize < NTFS_SB_SIZE) + memset(&outbuf[bufsize+2], 0, + NTFS_SB_SIZE - bufsize); outbuf[0] = 0xff; outbuf[1] = 0x3f; xout = NTFS_SB_SIZE + 2; @@ -343,9 +299,7 @@ static unsigned int ntfs_compress_block(const char *inbuf, xout = 0; errno = ENOMEM; } - return (xout); /* 0 for an error, > size if cannot compress */ -bug : - return (0); + return (xout); } /** @@ -1512,12 +1466,12 @@ static int ntfs_read_append(ntfs_attr *na, const runlist_element *rl, * or -1 if there were an irrecoverable error (errno set) */ -static int ntfs_flush(ntfs_attr *na, runlist_element *rl, s64 offs, +static s32 ntfs_flush(ntfs_attr *na, runlist_element *rl, s64 offs, const char *outbuf, s32 count, BOOL compress, BOOL appending, VCN *update_from) { - int rounded; - int written; + s32 rounded; + s32 written; int clsz; if (compress) { @@ -1655,7 +1609,7 @@ s64 ntfs_compressed_pwrite(ntfs_attr *na, runlist_element *wrl, s64 wpos, * (we are reopening an existing file to append to it) * Decompress the data and append */ - compsz = compressed_part << vol->cluster_size_bits; + compsz = (s32)compressed_part << vol->cluster_size_bits; outbuf = (char*)ntfs_malloc(na->compression_block_size); if (outbuf) { if (appending) { diff --git a/source/config.h b/source/config.h index a0a5da0..f145094 100644 --- a/source/config.h +++ b/source/config.h @@ -226,6 +226,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_SYS_BYTEORDER_H +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_DISK_H + /* Define to 1 if you have the header file. */ #undef HAVE_SYS_ENDIAN_H @@ -309,13 +312,13 @@ #define PACKAGE_NAME "ntfs-3g" /* Define to the full name and version of this package. */ -#define PACKAGE_STRING "ntfs-3g 2011.4.12" +#define PACKAGE_STRING "ntfs-3g 2012.1.15" /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "ntfs-3g" /* Define to the version of this package. */ -#define PACKAGE_VERSION "2011.4.12" +#define PACKAGE_VERSION "2012.1.15" /* POSIX ACL support */ #undef POSIXACLS @@ -345,7 +348,7 @@ #endif /* Version number of package */ -#define VERSION "2011.4.12" +#define VERSION "2012.1.15" /* Define to 1 if this is a Windows OS */ #undef WINDOWS @@ -375,6 +378,9 @@ /* Required define if using POSIX threads */ #undef _REENTRANT +/* Define to empty if `const' does not conform to ANSI C. */ +#undef const + /* Define to `__inline__' or `__inline' if that's what the C compiler calls it, or to nothing if 'inline' is not supported under any name. */ #ifndef __cplusplus diff --git a/source/debug.h b/source/debug.h index cf39b62..f7f3c6f 100644 --- a/source/debug.h +++ b/source/debug.h @@ -38,10 +38,10 @@ static __inline__ void ntfs_debug_runlist_dump(const struct _runlist_element *rl #define NTFS_BUG(msg) \ { \ - int ___i; \ + int ___i = 1; \ ntfs_log_critical("Bug in %s(): %s\n", __FUNCTION__, msg); \ ntfs_log_debug("Forcing segmentation fault!"); \ - ___i = ((int*)NULL)[1]; \ + ___i = ((int*)NULL)[___i]; \ } #endif /* defined _NTFS_DEBUG_H */ diff --git a/source/device.c b/source/device.c index db84010..274abac 100644 --- a/source/device.c +++ b/source/device.c @@ -58,6 +58,9 @@ #ifdef HAVE_SYS_MOUNT_H #include #endif +#ifdef HAVE_SYS_DISK_H +#include +#endif #ifdef HAVE_LINUX_FD_H #include #endif @@ -556,6 +559,36 @@ s64 ntfs_device_size_get(struct ntfs_device *dev, int block_size) return (s64)this_floppy.size * 512 / block_size; } } +#endif +#ifdef DIOCGMEDIASIZE + { + /* FreeBSD */ + off_t size; + + if (dev->d_ops->ioctl(dev, DIOCGMEDIASIZE, &size) >= 0) { + ntfs_log_debug("DIOCGMEDIASIZE nr bytes = %llu (0x%llx)\n", + (unsigned long long)size, + (unsigned long long)size); + return (s64)size / block_size; + } + } +#endif +#ifdef DKIOCGETBLOCKCOUNT + { + /* Mac OS X */ + uint64_t blocks; + int sector_size; + + sector_size = ntfs_device_sector_size_get(dev); + if (sector_size >= 0 && dev->d_ops->ioctl(dev, + DKIOCGETBLOCKCOUNT, &blocks) >= 0) + { + ntfs_log_debug("DKIOCGETBLOCKCOUNT nr blocks = %llu (0x%llx)\n", + (unsigned long long) blocks, + (unsigned long long) blocks); + return blocks * sector_size / block_size; + } + } #endif /* * We couldn't figure it out by using a specialized ioctl, @@ -705,6 +738,28 @@ int ntfs_device_sector_size_get(struct ntfs_device *dev) return sect_size; } } +#elif defined(DIOCGSECTORSIZE) + { + /* FreeBSD */ + size_t sect_size = 0; + + if (!dev->d_ops->ioctl(dev, DIOCGSECTORSIZE, §_size)) { + ntfs_log_debug("DIOCGSECTORSIZE sector size = %d bytes\n", + (int) sect_size); + return sect_size; + } + } +#elif defined(DKIOCGETBLOCKSIZE) + { + /* Mac OS X */ + uint32_t sect_size = 0; + + if (!dev->d_ops->ioctl(dev, DKIOCGETBLOCKSIZE, §_size)) { + ntfs_log_debug("DKIOCGETBLOCKSIZE sector size = %d bytes\n", + (int) sect_size); + return sect_size; + } + } #else errno = EOPNOTSUPP; #endif diff --git a/source/dir.c b/source/dir.c index 198bc29..ccae47c 100644 --- a/source/dir.c +++ b/source/dir.c @@ -257,7 +257,7 @@ u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni, u8 *index_end; ntfs_attr *ia_na; int eo, rc; - u32 index_block_size, index_vcn_size; + u32 index_block_size; u8 index_vcn_size_bits; ntfs_log_trace("Entering\n"); @@ -378,11 +378,9 @@ u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni, /* Determine the size of a vcn in the directory index. */ if (vol->cluster_size <= index_block_size) { - index_vcn_size = vol->cluster_size; index_vcn_size_bits = vol->cluster_size_bits; } else { - index_vcn_size = vol->sector_size; - index_vcn_size_bits = vol->sector_size_bits; + index_vcn_size_bits = NTFS_BLOCK_SIZE_BITS; } /* Get the starting vcn of the index_block holding the child node. */ @@ -1039,7 +1037,7 @@ int ntfs_readdir(ntfs_inode *dir_ni, s64 *pos, INDEX_ENTRY *ie; INDEX_ALLOCATION *ia = NULL; int rc, ir_pos, bmp_buf_size, bmp_buf_pos, eo; - u32 index_block_size, index_vcn_size; + u32 index_block_size; u8 index_block_size_bits, index_vcn_size_bits; ntfs_log_trace("Entering.\n"); @@ -1131,11 +1129,9 @@ int ntfs_readdir(ntfs_inode *dir_ni, s64 *pos, } index_block_size_bits = ffs(index_block_size) - 1; if (vol->cluster_size <= index_block_size) { - index_vcn_size = vol->cluster_size; index_vcn_size_bits = vol->cluster_size_bits; } else { - index_vcn_size = vol->sector_size; - index_vcn_size_bits = vol->sector_size_bits; + index_vcn_size_bits = NTFS_BLOCK_SIZE_BITS; } /* Are we jumping straight into the index allocation attribute? */ @@ -1517,7 +1513,7 @@ static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, le32 securid, else ir->clusters_per_index_block = ni->vol->indx_record_size >> - ni->vol->sector_size_bits; + NTFS_BLOCK_SIZE_BITS; ir->index.entries_offset = cpu_to_le32(sizeof(INDEX_HEADER)); ir->index.index_length = cpu_to_le32(index_len); ir->index.allocated_size = cpu_to_le32(index_len); @@ -2508,24 +2504,24 @@ int ntfs_set_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni, int res = 0; int longlen = 0; int shortlen = 0; - char newname[MAX_DOS_NAME_LENGTH + 1]; + char newname[3*MAX_DOS_NAME_LENGTH + 1]; ntfschar oldname[MAX_DOS_NAME_LENGTH]; int oldlen; - ntfs_volume *vol; - u64 fnum; u64 dnum; BOOL closed = FALSE; ntfschar *shortname = NULL; ntfschar longname[NTFS_MAX_NAME_LEN]; - vol = ni->vol; - fnum = ni->mft_no; - /* convert the string to the NTFS wide chars */ - if (size > MAX_DOS_NAME_LENGTH) - size = MAX_DOS_NAME_LENGTH; + /* copy the string to insert a null char, and truncate */ + if (size > 3*MAX_DOS_NAME_LENGTH) + size = 3*MAX_DOS_NAME_LENGTH; strncpy(newname, value, size); + /* a long name may be truncated badly and be untranslatable */ newname[size] = 0; + /* convert the string to the NTFS wide chars, and truncate */ shortlen = ntfs_mbstoucs(newname, &shortname); + if (shortlen > MAX_DOS_NAME_LENGTH) + shortlen = MAX_DOS_NAME_LENGTH; /* make sure the short name has valid chars */ if ((shortlen < 0) || ntfs_forbidden_chars(shortname,shortlen)) { ntfs_inode_close_in_dir(ni,dir_ni); diff --git a/source/efs.c b/source/efs.c index 6ccec20..7957005 100644 --- a/source/efs.c +++ b/source/efs.c @@ -139,7 +139,6 @@ static int fixup_loop(ntfs_inode *ni) ntfs_attr *na; ATTR_RECORD *a; BOOL restart; - BOOL first; int cnt; int maxcnt; int res = 0; @@ -200,7 +199,6 @@ static int fixup_loop(ntfs_inode *ni) if (na) ntfs_attr_close(na); } - first = FALSE; } while (restart && !res); if (ctx) ntfs_attr_put_search_ctx(ctx); diff --git a/source/index.c b/source/index.c index 01438a0..d498dde 100644 --- a/source/index.c +++ b/source/index.c @@ -701,7 +701,7 @@ int ntfs_index_lookup(const void *key, const int key_len, ntfs_index_context *ic if (ni->vol->cluster_size <= icx->block_size) icx->vcn_size_bits = ni->vol->cluster_size_bits; else - icx->vcn_size_bits = ni->vol->sector_size_bits; + icx->vcn_size_bits = NTFS_BLOCK_SIZE_BITS; /* get the appropriate collation function */ icx->collate = ntfs_get_collate_function(ir->collation_rule); if (!icx->collate) { diff --git a/source/inode.c b/source/inode.c index c51e523..a4a0134 100644 --- a/source/inode.c +++ b/source/inode.c @@ -437,13 +437,12 @@ static int idata_cache_compare(const struct CACHED_GENERIC *cached, void ntfs_inode_invalidate(ntfs_volume *vol, const MFT_REF mref) { struct CACHED_NIDATA item; - int count; item.inum = MREF(mref); item.ni = (ntfs_inode*)NULL; item.pathname = (const char*)NULL; item.varsize = 0; - count = ntfs_invalidate_cache(vol->nidata_cache, + ntfs_invalidate_cache(vol->nidata_cache, GENERIC(&item),idata_cache_compare,CACHE_FREE); } @@ -574,6 +573,9 @@ int ntfs_inode_close(ntfs_inode *ni) ntfs_inode *ntfs_extent_inode_open(ntfs_inode *base_ni, const MFT_REF mref) { u64 mft_no = MREF_LE(mref); + VCN extent_vcn; + runlist_element *rl; + ntfs_volume *vol; ntfs_inode *ni = NULL; ntfs_inode **extent_nis; int i; @@ -588,6 +590,37 @@ ntfs_inode *ntfs_extent_inode_open(ntfs_inode *base_ni, const MFT_REF mref) (unsigned long long)mft_no, (unsigned long long)base_ni->mft_no); + if (!base_ni->mft_no) { + /* + * When getting extents of MFT, we must be sure + * they are in the MFT part which has already + * been mapped, otherwise we fall into an endless + * recursion. + * Situations have been met where extents locations + * are described in themselves. + * This is a severe error which chkdsk cannot fix. + */ + vol = base_ni->vol; + extent_vcn = mft_no << vol->mft_record_size_bits + >> vol->cluster_size_bits; + rl = vol->mft_na->rl; + if (rl) { + while (rl->length + && ((rl->vcn + rl->length) <= extent_vcn)) + rl++; + } + if (!rl || (rl->lcn < 0)) { + ntfs_log_error("MFT is corrupt, cannot read" + " its unmapped extent record %lld\n", + (long long)mft_no); + ntfs_log_error("Note : chkdsk cannot fix this," + " try ntfsfix\n"); + errno = EIO; + ni = (ntfs_inode*)NULL; + goto out; + } + } + /* Is the extent inode already open and attached to the base inode? */ if (base_ni->nr_extents > 0) { extent_nis = base_ni->extent_nis; diff --git a/source/layout.h b/source/layout.h index daff97d..c167135 100644 --- a/source/layout.h +++ b/source/layout.h @@ -2223,11 +2223,11 @@ typedef struct { /* The below field is NOT present for the quota defaults entry. */ SID sid; /* The SID of the user/object associated with this quota entry. If this field is missing - then the INDEX_ENTRY is padded with zeros - to multiply of 8 which are not counted in + then the INDEX_ENTRY is padded to a multiple + of 8 with zeros which are not counted in the data_length field. If the sid is present then this structure is padded with zeros to - multiply of 8 and the padding is counted in + a multiple of 8 and the padding is counted in the INDEX_ENTRY's data_length. */ } __attribute__((__packed__)) QUOTA_CONTROL_ENTRY; diff --git a/source/logfile.c b/source/logfile.c index 0f8bce9..53d8d68 100644 --- a/source/logfile.c +++ b/source/logfile.c @@ -468,7 +468,7 @@ BOOL ntfs_check_logfile(ntfs_attr *log_na, RESTART_PAGE_HEADER **rp) u8 *kaddr = NULL; RESTART_PAGE_HEADER *rstr1_ph = NULL; RESTART_PAGE_HEADER *rstr2_ph = NULL; - int log_page_size, log_page_mask, err; + int log_page_size, err; BOOL logfile_is_empty = TRUE; u8 log_page_bits; @@ -481,7 +481,6 @@ BOOL ntfs_check_logfile(ntfs_attr *log_na, RESTART_PAGE_HEADER **rp) if (size > (s64)MaxLogFileSize) size = MaxLogFileSize; log_page_size = DefaultLogPageSize; - log_page_mask = log_page_size - 1; /* * Use generic_ffs() instead of ffs() to enable the compiler to * optimize log_page_size and log_page_bits into constants. @@ -703,7 +702,7 @@ int ntfs_empty_logfile(ntfs_attr *na) char buf[NTFS_BUF_SIZE]; ntfs_log_trace("Entering.\n"); - + if (NVolLogFileEmpty(na->ni->vol)) return 0; @@ -717,7 +716,7 @@ int ntfs_empty_logfile(ntfs_attr *na) pos = 0; while ((count = na->data_size - pos) > 0) { - + if (count > NTFS_BUF_SIZE) count = NTFS_BUF_SIZE; @@ -733,6 +732,6 @@ int ntfs_empty_logfile(ntfs_attr *na) } NVolSetLogFileEmpty(na->ni->vol); - + return 0; } diff --git a/source/mft.c b/source/mft.c index e93c664..0640efe 100644 --- a/source/mft.c +++ b/source/mft.c @@ -216,8 +216,10 @@ int ntfs_mft_record_check(const ntfs_volume *vol, const MFT_REF mref, int ret = -1; if (!ntfs_is_file_record(m->magic)) { - ntfs_log_error("Record %llu has no FILE magic (0x%x)\n", - (unsigned long long)MREF(mref), *(le32 *)m); + if (!NVolNoFixupWarn(vol)) + ntfs_log_error("Record %llu has no FILE magic (0x%x)\n", + (unsigned long long)MREF(mref), + (int)le32_to_cpu(*(le32*)m)); goto err_out; } @@ -1190,7 +1192,7 @@ undo_alloc: static int ntfs_mft_record_init(ntfs_volume *vol, s64 size) { int ret = -1; - ntfs_attr *mft_na, *mftbmp_na; + ntfs_attr *mft_na; s64 old_data_initialized, old_data_size; ntfs_attr_search_ctx *ctx; @@ -1199,7 +1201,6 @@ static int ntfs_mft_record_init(ntfs_volume *vol, s64 size) /* NOTE: Caller must sanity check vol, vol->mft_na and vol->mftbmp_na */ mft_na = vol->mft_na; - mftbmp_na = vol->mftbmp_na; /* * The mft record is outside the initialized data. Extend the mft data @@ -1295,14 +1296,13 @@ undo_data_init: static int ntfs_mft_rec_init(ntfs_volume *vol, s64 size) { int ret = -1; - ntfs_attr *mft_na, *mftbmp_na; + ntfs_attr *mft_na; s64 old_data_initialized, old_data_size; ntfs_attr_search_ctx *ctx; ntfs_log_enter("Entering\n"); mft_na = vol->mft_na; - mftbmp_na = vol->mftbmp_na; if (size > mft_na->allocated_size || size > mft_na->initialized_size) { errno = EIO; diff --git a/source/mst.c b/source/mst.c index 470942d..b7937b7 100644 --- a/source/mst.c +++ b/source/mst.c @@ -47,7 +47,8 @@ * EIO Multi sector transfer error was detected. Magic of the NTFS * record in @b will have been set to "BAAD". */ -int ntfs_mst_post_read_fixup(NTFS_RECORD *b, const u32 size) +int ntfs_mst_post_read_fixup_warn(NTFS_RECORD *b, const u32 size, + BOOL warn) { u16 usa_ofs, usa_count, usn; u16 *usa_pos, *data_pos; @@ -63,9 +64,14 @@ int ntfs_mst_post_read_fixup(NTFS_RECORD *b, const u32 size) (u32)(usa_ofs + (usa_count * 2)) > size || (size >> NTFS_BLOCK_SIZE_BITS) != usa_count) { errno = EINVAL; - ntfs_log_perror("%s: magic: 0x%08x size: %d usa_ofs: %d " - "usa_count: %d", __FUNCTION__, *(le32 *)b, - size, usa_ofs, usa_count); + if (warn) { + ntfs_log_perror("%s: magic: 0x%08lx size: %ld " + " usa_ofs: %d usa_count: %u", + __FUNCTION__, + (long)le32_to_cpu(*(le32 *)b), + (long)size, (int)usa_ofs, + (unsigned int)usa_count); + } return -1; } /* Position of usn in update sequence array. */ @@ -118,6 +124,16 @@ int ntfs_mst_post_read_fixup(NTFS_RECORD *b, const u32 size) return 0; } +/* + * Deprotect multi sector transfer protected data + * with a warning if an error is found. + */ + +int ntfs_mst_post_read_fixup(NTFS_RECORD *b, const u32 size) +{ + return (ntfs_mst_post_read_fixup_warn(b,size,TRUE)); +} + /** * ntfs_mst_pre_write_fixup - apply multi sector transfer protection * @b: pointer to the data to protect diff --git a/source/mst.h b/source/mst.h index ca81382..d6ca6f2 100644 --- a/source/mst.h +++ b/source/mst.h @@ -25,8 +25,11 @@ #include "types.h" #include "layout.h" +#include "volume.h" extern int ntfs_mst_post_read_fixup(NTFS_RECORD *b, const u32 size); +extern int ntfs_mst_post_read_fixup_warn(NTFS_RECORD *b, const u32 size, + BOOL warn); extern int ntfs_mst_pre_write_fixup(NTFS_RECORD *b, const u32 size); extern void ntfs_mst_post_write_fixup(NTFS_RECORD *b); diff --git a/source/ntfstime.h b/source/ntfstime.h index 426269d..21c222d 100644 --- a/source/ntfstime.h +++ b/source/ntfstime.h @@ -36,6 +36,18 @@ #include "types.h" +#ifndef GEKKO +/* + * assume "struct timespec" is not defined if st_mtime is not defined + */ +#if !defined(st_mtime) & !defined(__timespec_defined) +struct timespec { + time_t tv_sec; + long tv_nsec; +} ; +#endif +#endif + /* * There are four times more conversions of internal representation * to ntfs representation than any other conversion, so the most diff --git a/source/object_id.c b/source/object_id.c index 8799ddb..059e882 100644 --- a/source/object_id.c +++ b/source/object_id.c @@ -287,7 +287,6 @@ static int remove_object_id_index(ntfs_attr *na, ntfs_index_context *xo, if (size >= (s64)sizeof(GUID)) { memcpy(&key.object_id, &old_attr->object_id,sizeof(GUID)); - size = sizeof(GUID); if (!ntfs_index_lookup(&key, sizeof(OBJECT_ID_INDEX_KEY), xo)) { entry = (struct OBJECT_ID_INDEX*)xo->entry; @@ -300,7 +299,6 @@ static int remove_object_id_index(ntfs_attr *na, ntfs_index_context *xo, memcpy(&old_attr->domain_id, &entry->data.domain_id, sizeof(GUID)); - size = sizeof(OBJECT_ID_ATTR); if (ntfs_index_rm(xo)) ret = -1; } diff --git a/source/param.h b/source/param.h index 7420c79..c3675b7 100644 --- a/source/param.h +++ b/source/param.h @@ -50,6 +50,19 @@ enum { /* maximum cluster size for allowing compression for new files */ #define MAX_COMPRESSION_CLUSTER_SIZE 4096 +/* + * Use of big write buffers + * + * With small volumes, the cluster allocator may fail to allocate + * enough clusters when the volume is nearly full. At most a run + * can be allocated per bitmap chunk. So, there is a danger when the + * number of chunks (capacity/(32768*clsiz)) is less than the number + * of clusters in the biggest write buffer (131072/clsiz). Hence + * a safe minimal capacity is 4GB + */ + +#define SAFE_CAPACITY_FOR_BIG_WRITES 0x100000000LL + /* * Parameters for runlists */ @@ -63,6 +76,11 @@ enum { #define XATTRMAPPINGFILE ".NTFS-3G/XattrMapping" /* default mapping file */ +/* + * Parameters for path canonicalization + */ + +#define MAPPERNAMELTH 256 /* * Permission checking modes for high level and low level diff --git a/source/realpath.c b/source/realpath.c new file mode 100644 index 0000000..a93bc69 --- /dev/null +++ b/source/realpath.c @@ -0,0 +1,103 @@ +/* + * realpath.c - realpath() aware of device mapper + * Originated from the util-linux project. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#ifdef HAVE_LIMITS_H +#include +#endif +#ifdef HAVE_CTYPE_H +#include +#endif + +#include "param.h" +#include "realpath.h" + +/* If there is no realpath() on the system, provide a dummy one. */ +#ifndef HAVE_REALPATH +char *ntfs_realpath(const char *path, char *resolved_path) +{ + strncpy(resolved_path, path, PATH_MAX); + resolved_path[PATH_MAX] = '\0'; + return resolved_path; +} +#endif + + +#ifdef linux + +/* + * Converts private "dm-N" names to "/dev/mapper/" + * + * Since 2.6.29 (patch 784aae735d9b0bba3f8b9faef4c8b30df3bf0128) kernel sysfs + * provides the real DM device names in /sys/block//dm/name + */ +static char * +canonicalize_dm_name(const char *ptname, char *canonical) +{ + FILE *f; + size_t sz; + char path[MAPPERNAMELTH + 24]; + char name[MAPPERNAMELTH + 16]; + char *res = NULL; + + snprintf(path, sizeof(path), "/sys/block/%s/dm/name", ptname); + if (!(f = fopen(path, "r"))) + return NULL; + + /* read "\n" from sysfs */ + if (fgets(name, sizeof(name), f) && (sz = strlen(name)) > 1) { + name[sz - 1] = '\0'; + snprintf(path, sizeof(path), "/dev/mapper/%s", name); + res = strcpy(canonical, path); + } + fclose(f); + return res; +} + +/* + * Canonicalize a device path + * + * Workaround from "basinilya" for fixing device mapper paths. + * + * Background (Phillip Susi, 2011-04-09) + * - ntfs-3g canonicalizes the device name so that if you mount with + * /dev/mapper/foo, the device name listed in mtab is /dev/dm-n, + * so you can not umount /dev/mapper/foo + * - umount won't even recognize and translate /dev/dm-n to the mount + * point, apparently because of the '-' involved. Editing mtab and + * removing the '-' allows you to umount /dev/dmn successfully. + * + * This code restores the devmapper name after canonicalization, + * until a proper fix is implemented. + */ + +char *ntfs_realpath_canonicalize(const char *path, char *canonical) +{ + char *p; + + if (path == NULL) + return NULL; + + if (!ntfs_realpath(path, canonical)) + return NULL; + + p = strrchr(canonical, '/'); + if (p && strncmp(p, "/dm-", 4) == 0 && isdigit(*(p + 4))) { + p = canonicalize_dm_name(p+1, canonical); + if (p) + return p; + } + + return canonical; +} + +#endif diff --git a/source/realpath.h b/source/realpath.h new file mode 100644 index 0000000..970d2af --- /dev/null +++ b/source/realpath.h @@ -0,0 +1,24 @@ +/* + * realpath.h - realpath() aware of device mapper + */ + +#ifndef REALPATH_H +#define REALPATH_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_REALPATH +#define ntfs_realpath realpath +#else +extern char *ntfs_realpath(const char *path, char *resolved_path); +#endif + +#ifdef linux +extern char *ntfs_realpath_canonicalize(const char *path, char *resolved_path); +#else +#define ntfs_realpath_canonicalize ntfs_realpath +#endif + +#endif /* REALPATH_H */ diff --git a/source/runlist.c b/source/runlist.c index 383a80b..7e158d4 100644 --- a/source/runlist.c +++ b/source/runlist.c @@ -136,9 +136,10 @@ runlist_element *ntfs_rl_extend(ntfs_attr *na, runlist_element *rl, if (!newrl) { errno = ENOMEM; rl = (runlist_element*)NULL; - } else + } else { na->rl = newrl; rl = &newrl[irl]; + } } else { ntfs_log_error("Cannot extend unmapped runlist"); errno = EIO; @@ -1623,7 +1624,7 @@ errno_set: int ntfs_rl_truncate(runlist **arl, const VCN start_vcn) { runlist *rl; - BOOL is_end = FALSE; + /* BOOL is_end = FALSE; */ if (!arl || !*arl) { errno = EINVAL; @@ -1666,8 +1667,10 @@ int ntfs_rl_truncate(runlist **arl, const VCN start_vcn) */ if (rl->length) { ++rl; +/* if (!rl->length) is_end = TRUE; +*/ rl->vcn = start_vcn; rl->length = 0; } @@ -1675,7 +1678,7 @@ int ntfs_rl_truncate(runlist **arl, const VCN start_vcn) /** * Reallocate memory if necessary. * FIXME: Below code is broken, because runlist allocations must be - * a multiply of 4096. The code caused crashes and corruptions. + * a multiple of 4096. The code caused crashes and corruptions. */ /* if (!is_end) { diff --git a/source/security.c b/source/security.c index b0bbe6b..5e70c21 100644 --- a/source/security.c +++ b/source/security.c @@ -1064,7 +1064,6 @@ static int upgrade_secur_desc(ntfs_volume *vol, na = ntfs_attr_open(ni, AT_STANDARD_INFORMATION, AT_UNNAMED, 0); if (na) { - res = 0; /* expand standard information attribute to v3.x */ res = ntfs_attr_truncate(na, (s64)sizeof(STANDARD_INFORMATION)); @@ -2013,7 +2012,6 @@ int ntfs_get_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, const SID *gsid; /* group of file/directory */ uid_t uid; gid_t gid; - int perm; BOOL isdir; size_t outsize; @@ -2048,7 +2046,6 @@ int ntfs_get_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, * fetch owner and group for cacheing */ if (pxdesc) { - perm = pxdesc->mode & 07777; /* * Create a security id if there were none * and upgrade option is selected @@ -2062,11 +2059,10 @@ int ntfs_get_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, #if OWNERFROMACL uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); #else - if (!perm && ntfs_same_sid(usid, adminsid)) { + if (!(pxdesc->mode & 07777) + && ntfs_same_sid(usid, adminsid)) { uid = find_tenant(scx, securattr); - if (uid) - perm = 0700; } else uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); #endif @@ -2893,7 +2889,6 @@ int ntfs_set_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, uid_t uid; uid_t gid; int res; - mode_t mode; BOOL isdir; BOOL deflt; BOOL exist; @@ -2919,7 +2914,6 @@ int ntfs_set_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, gid = cached->gid; oldpxdesc = cached->pxdesc; if (oldpxdesc) { - mode = oldpxdesc->mode; newpxdesc = ntfs_replace_acl(oldpxdesc, (const struct POSIX_ACL*)value,count,deflt); } @@ -2946,7 +2940,6 @@ int ntfs_set_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, || (!exist && (flags & XATTR_REPLACE))) { errno = (exist ? EEXIST : ENODATA); } else { - mode = oldpxdesc->mode; newpxdesc = ntfs_replace_acl(oldpxdesc, (const struct POSIX_ACL*)value,count,deflt); } @@ -3513,16 +3506,16 @@ int ntfs_set_owner(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, int ntfs_set_ownmod(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, uid_t uid, gid_t gid, const mode_t mode) { - const SECURITY_DESCRIPTOR_RELATIVE *phead; const struct CACHED_PERMISSIONS *cached; char *oldattr; - const SID *usid; - const SID *gsid; uid_t fileuid; uid_t filegid; - BOOL isdir; int res; #if POSIXACLS + const SECURITY_DESCRIPTOR_RELATIVE *phead; + const SID *usid; + const SID *gsid; + BOOL isdir; const struct POSIX_SECURITY *oldpxdesc; struct POSIX_SECURITY *newpxdesc = (struct POSIX_SECURITY*)NULL; int pxsize; @@ -3555,6 +3548,7 @@ int ntfs_set_ownmod(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, filegid = 0; oldattr = getsecurityattr(scx->vol, ni); if (oldattr) { +#if POSIXACLS isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0); phead = (const SECURITY_DESCRIPTOR_RELATIVE*) @@ -3567,7 +3561,6 @@ int ntfs_set_ownmod(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, usid = (const SID*) &oldattr[le32_to_cpu(phead->owner)]; #endif -#if POSIXACLS newpxdesc = ntfs_build_permissions_posix(scx->mapping, oldattr, usid, gsid, isdir); if (!newpxdesc || ntfs_merge_mode_posix(newpxdesc, mode)) @@ -4784,6 +4777,7 @@ BOOL ntfs_set_file_attributes(struct SECURITY_API *scapi, ni->flags = (ni->flags & ~settable) | (cpu_to_le32(attrib) & settable); NInoSetDirty(ni); + NInoFileNameSetDirty(ni); } if (!ntfs_inode_close(ni)) res = -1; diff --git a/source/types.h b/source/types.h index 77758ea..fcd1360 100644 --- a/source/types.h +++ b/source/types.h @@ -1,5 +1,5 @@ /* - * types.h - Misc type definitions not related to on-disk structure. + * types.h - Misc type definitions not related to on-disk structure. * Originated from the Linux-NTFS project. * * Copyright (c) 2000-2004 Anton Altaparmakov diff --git a/source/unistr.c b/source/unistr.c index ffaabe0..c0ac342 100644 --- a/source/unistr.c +++ b/source/unistr.c @@ -1262,7 +1262,7 @@ void ntfs_upcase_table_build(ntfschar *uc, u32 uc_len) u32 ntfs_upcase_build_default(ntfschar **upcase) { - u32 upcase_len; + u32 upcase_len = 0; *upcase = (ntfschar*)ntfs_malloc(UPCASE_LEN*2); if (*upcase) { @@ -1406,16 +1406,18 @@ BOOL ntfs_collapsible_chars(ntfs_volume *vol, { BOOL collapsible; unsigned int ch; + unsigned int cs; int i; collapsible = shortlen == longlen; - if (collapsible) - for (i=0; i= vol->upcase_len) - || ((shortname[i] != longname[i]) - && (shortname[i] != vol->upcase[ch]))) - collapsible = FALSE; + for (i=0; collapsible && (i= vol->upcase_len) + || (cs >= vol->upcase_len) + || (vol->upcase[cs] != vol->upcase[ch]))) + collapsible = FALSE; } return (collapsible); } diff --git a/source/volume.c b/source/volume.c index 28e4c90..a18109f 100644 --- a/source/volume.c +++ b/source/volume.c @@ -54,6 +54,7 @@ #include #endif +#include "param.h" #include "compat.h" #include "volume.h" #include "attrib.h" @@ -67,6 +68,7 @@ #include "dir.h" #include "logging.h" #include "cache.h" +#include "realpath.h" #include "misc.h" const char *ntfs_home = @@ -373,6 +375,12 @@ mft_has_no_attr_list: /* Done with the $Mft mft record. */ ntfs_attr_put_search_ctx(ctx); ctx = NULL; + + /* Update the size fields in the inode. */ + vol->mft_ni->data_size = vol->mft_na->data_size; + vol->mft_ni->allocated_size = vol->mft_na->allocated_size; + set_nino_flag(vol->mft_ni, KnownSize); + /* * The volume is now setup so we can use all read access functions. */ @@ -494,6 +502,12 @@ ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev, unsigned long flags) NVolSetShowSysFiles(vol); NVolSetShowHidFiles(vol); NVolClearHideDotFiles(vol); + /* set default compression */ +#if DEFAULT_COMPRESSION + NVolSetCompression(vol); +#else + NVolClearCompression(vol); +#endif if (flags & MS_RDONLY) NVolSetReadOnly(vol); @@ -1359,18 +1373,6 @@ int ntfs_umount(ntfs_volume *vol, const BOOL force __attribute__((unused))) #ifdef HAVE_MNTENT_H -#ifndef HAVE_REALPATH -/** - * realpath - If there is no realpath on the system - */ -static char *realpath(const char *path, char *resolved_path) -{ - strncpy(resolved_path, path, PATH_MAX); - resolved_path[PATH_MAX] = '\0'; - return resolved_path; -} -#endif - /** * ntfs_mntent_check - desc * @@ -1394,7 +1396,7 @@ static int ntfs_mntent_check(const char *file, unsigned long *mnt_flags) err = errno; goto exit; } - if (!realpath(file, real_file)) { + if (!ntfs_realpath_canonicalize(file, real_file)) { err = errno; goto exit; } @@ -1403,7 +1405,7 @@ static int ntfs_mntent_check(const char *file, unsigned long *mnt_flags) goto exit; } while ((mnt = getmntent(f))) { - if (!realpath(mnt->mnt_fsname, real_fsname)) + if (!ntfs_realpath_canonicalize(mnt->mnt_fsname, real_fsname)) continue; if (!strcmp(real_file, real_fsname)) break; @@ -1730,3 +1732,113 @@ int ntfs_volume_get_free_space(ntfs_volume *vol) } return (ret); } + +/** + * ntfs_volume_rename - change the current label on a volume + * @vol: volume to change the label on + * @label: the new label + * @label_len: the length of @label in ntfschars including the terminating NULL + * character, which is mandatory (the value can not exceed 128) + * + * Change the label on the volume @vol to @label. + */ +int ntfs_volume_rename(ntfs_volume *vol, ntfschar *label, int label_len) +{ + ntfs_attr *na; + char *old_vol_name; + char *new_vol_name = NULL; + int new_vol_name_len; + int err; + + if (NVolReadOnly(vol)) { + ntfs_log_error("Refusing to change label on read-only mounted " + "volume.\n"); + errno = EROFS; + return -1; + } + + label_len *= sizeof(ntfschar); + if (label_len > 0x100) { + ntfs_log_error("New label is too long. Maximum %u characters " + "allowed.\n", + (unsigned)(0x100 / sizeof(ntfschar))); + errno = ERANGE; + return -1; + } + + na = ntfs_attr_open(vol->vol_ni, AT_VOLUME_NAME, AT_UNNAMED, 0); + if (!na) { + if (errno != ENOENT) { + err = errno; + ntfs_log_perror("Lookup of $VOLUME_NAME attribute " + "failed"); + goto err_out; + } + + /* The volume name attribute does not exist. Need to add it. */ + if (ntfs_attr_add(vol->vol_ni, AT_VOLUME_NAME, AT_UNNAMED, 0, + (u8*) label, label_len)) + { + err = errno; + ntfs_log_perror("Encountered error while adding " + "$VOLUME_NAME attribute"); + goto err_out; + } + } + else { + s64 written; + + if (NAttrNonResident(na)) { + err = errno; + ntfs_log_error("Error: Attribute $VOLUME_NAME must be " + "resident.\n"); + goto err_out; + } + + if (na->data_size != label_len) { + if (ntfs_attr_truncate(na, label_len)) { + err = errno; + ntfs_log_perror("Error resizing resident " + "attribute"); + goto err_out; + } + } + + if (label_len) { + written = ntfs_attr_pwrite(na, 0, label_len, label); + if (written == -1) { + err = errno; + ntfs_log_perror("Error when writing " + "$VOLUME_NAME data"); + goto err_out; + } + else if (written != label_len) { + err = EIO; + ntfs_log_error("Partial write when writing " + "$VOLUME_NAME data."); + goto err_out; + + } + } + } + + new_vol_name_len = + ntfs_ucstombs(label, label_len, &new_vol_name, 0); + if (new_vol_name_len == -1) { + err = errno; + ntfs_log_perror("Error while decoding new volume name"); + goto err_out; + } + + old_vol_name = vol->vol_name; + vol->vol_name = new_vol_name; + free(old_vol_name); + + err = 0; +err_out: + if (na) + ntfs_attr_close(na); + if (err) + errno = err; + return err ? -1 : 0; +} diff --git a/source/volume.h b/source/volume.h index b3f47bf..9d62d66 100644 --- a/source/volume.h +++ b/source/volume.h @@ -113,6 +113,7 @@ typedef enum { NV_ShowHidFiles, /* 1: Show files marked hidden. */ NV_HideDotFiles, /* 1: Set hidden flag on dot files */ NV_Compression, /* 1: allow compression */ + NV_NoFixupWarn, /* 1: Do not log fixup errors */ } ntfs_volume_state_bits; #define test_nvol_flag(nv, flag) test_bit(NV_##flag, (nv)->state) @@ -147,6 +148,10 @@ typedef enum { #define NVolSetCompression(nv) set_nvol_flag(nv, Compression) #define NVolClearCompression(nv) clear_nvol_flag(nv, Compression) +#define NVolNoFixupWarn(nv) test_nvol_flag(nv, NoFixupWarn) +#define NVolSetNoFixupWarn(nv) set_nvol_flag(nv, NoFixupWarn) +#define NVolClearNoFixupWarn(nv) clear_nvol_flag(nv, NoFixupWarn) + /* * NTFS version 1.1 and 1.2 are used by Windows NT4. * NTFS version 2.x is used by Windows 2000 Beta @@ -297,6 +302,7 @@ extern int ntfs_volume_error(int err); extern void ntfs_mount_error(const char *vol, const char *mntpoint, int err); extern int ntfs_volume_get_free_space(ntfs_volume *vol); +extern int ntfs_volume_rename(ntfs_volume *vol, ntfschar *label, int label_len); extern int ntfs_set_shown_files(ntfs_volume *vol, BOOL show_sys_files, BOOL show_hid_files, BOOL hide_dot_files);