http://linux-ntfs.sourceforge.net/snapshots/ntfsprogs-200307311516.tar.bz2
[ntfsprogs.git] / libntfs / mft.c
1 /*
2  * mft.c - Mft record handling code. Part of the Linux-NTFS project.
3  *
4  * Copyright (c) 2000-2003 Anton Altaparmakov
5  *
6  * This program/include file is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as published
8  * by the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program/include file is distributed in the hope that it will be
12  * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
13  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program (in the main directory of the Linux-NTFS
18  * distribution in the file COPYING); if not, write to the Free Software
19  * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <errno.h>
25 #include <string.h>
26
27 #include "compat.h"
28
29 #include "types.h"
30 #include "disk_io.h"
31 #include "debug.h"
32 #include "bitmap.h"
33 #include "attrib.h"
34 #include "inode.h"
35 #include "volume.h"
36 #include "layout.h"
37 #include "mft.h"
38
39 /**
40  * ntfs_mft_records_read - read records from the mft from disk
41  * @vol:        volume to read from
42  * @mref:       starting mft record number to read
43  * @count:      number of mft records to read
44  * @b:          output data buffer
45  *
46  * Read @count mft records starting at @mref from volume @vol into buffer
47  * @b. Return 0 on success or -1 on error, with errno set to the error
48  * code.
49  *
50  * The read mft records are mst deprotected and are hence ready to use. The
51  * caller should check each record with is_baad_record() in case mst
52  * deprotection failed.
53  *
54  * NOTE: @b has to be at least of size @count * vol->mft_record_size.
55  */
56 int ntfs_mft_records_read(const ntfs_volume *vol, const MFT_REF mref,
57                 const s64 count, MFT_RECORD *b)
58 {
59         s64 br;
60         VCN m;
61
62         Dprintf("%s(): Entering for inode 0x%Lx.\n", __FUNCTION__, MREF(mref));
63         if (!vol || !vol->mft_na || !b || count < 0) {
64                 errno = EINVAL;
65                 return -1;
66         }
67         m = MREF(mref);
68         if (m + count > vol->nr_mft_records) {
69                 errno = ESPIPE;
70                 return -1;
71         }
72         br = ntfs_attr_mst_pread(vol->mft_na, m << vol->mft_record_size_bits,
73                         count, vol->mft_record_size, b);
74         if (br != count) {
75                 if (br != -1)
76                         errno = EIO;
77                 if (br >= 0)
78                         Dputs("Error: partition is smaller than it should be!");
79                 else
80                         Dperror("Error reading $Mft record(s)");
81                 return -1;
82         }
83         return 0;
84 }
85
86 /**
87  * ntfs_mft_records_write - write mft records to disk
88  * @vol:        volume to write to
89  * @mref:       starting mft record number to write
90  * @count:      number of mft records to write
91  * @b:          data buffer containing the mft records to write
92  *
93  * Write @count mft records starting at @mref from data buffer @b to volume
94  * @vol. Return 0 on success or -1 on error, with errno set to the error code.
95  *
96  * Before the mft records are written, they are mst protected. After the write,
97  * they are deprotected again, thus resulting in an increase in the update
98  * sequence number inside the data buffer @b.
99  *
100  * If any mft records are written which are also represented in the mft mirror
101  * $MFTMirr, we make a copy of the relevant parts of the data buffer @b into a
102  * temporary buffer before we do the actual write. Then if at least one mft
103  * record was successfully written, we write the appropriate mft records from
104  * the copied buffer to the mft mirror, too.
105  */
106 int ntfs_mft_records_write(const ntfs_volume *vol, const MFT_REF mref,
107                 const s64 count, MFT_RECORD *b)
108 {
109         s64 bw;
110         VCN m;
111         void *bmirr = NULL;
112         int cnt = 0, res = 0;
113
114         Dprintf("%s(): Entering for inode 0x%Lx.\n", __FUNCTION__, MREF(mref));
115         if (!vol || !vol->mft_na || !b || count < 0) {
116                 errno = EINVAL;
117                 return -1;
118         }
119         m = MREF(mref);
120         if (m < vol->mftmirr_size) {
121                 cnt = vol->mftmirr_size - m;
122                 if (cnt > count)
123                         cnt = count;
124                 bmirr = malloc(cnt * vol->mft_record_size);
125                 if (!bmirr)
126                         return -1;
127                 memcpy(bmirr, b, cnt * vol->mft_record_size);
128         }
129         if (m + count > vol->nr_mft_records) {
130                 // TODO: Need to extend $MFT. This is not just normal attribute
131                 // extension as many rules need to be observed. (AIA)
132                 if (bmirr);
133                         free(bmirr);
134                 errno = ENOTSUP;
135                 return -1;
136         }
137         bw = ntfs_attr_mst_pwrite(vol->mft_na, m << vol->mft_record_size_bits,
138                         count, vol->mft_record_size, b);
139         if (bw != count) {
140                 if (bw != -1)
141                         errno = EIO;
142                 if (bw >= 0)
143                         Dputs("Error: partial write while writing $Mft "
144                                         "record(s)!\n");
145                 else
146                         Dperror("Error writing $Mft record(s)");
147                 res = errno;
148         }
149         if (bmirr && bw > 0) {
150                 if (bw < cnt)
151                         cnt = bw;
152                 bw = ntfs_attr_mst_pwrite(vol->mftmirr_na,
153                                 m << vol->mft_record_size_bits, cnt,
154                                 vol->mft_record_size, bmirr);
155                 if (bw != cnt) {
156                         if (bw != -1)
157                                 errno = EIO;
158                         Dputs("Error: failed to sync $MFTMirr! Run chkdsk.");
159                         res = errno;
160                 }
161         }
162         if (bmirr)
163                 free(bmirr);
164         if (!res)
165                 return res;
166         errno = res;
167         return -1;
168 }
169
170 /**
171  * ntfs_file_record_read - read a FILE record from the mft from disk
172  * @vol:        volume to read from
173  * @mref:       mft reference specifying mft record to read
174  * @mrec:       address of pointer in which to return the mft record
175  * @attr:       address of pointer in which to return the first attribute
176  *
177  * Read a FILE record from the mft of @vol from the storage medium. @mref
178  * specifies the mft record to read, including the sequence number, which can
179  * be 0 if no sequence number checking is to be performed.
180  *
181  * The function allocates a buffer large enough to hold the mft record and
182  * reads the record into the buffer (mst deprotecting it in the process).
183  * *@mrec is then set to point to the buffer.
184  *
185  * If @attr is not NULL, *@attr is set to point to the first attribute in the
186  * mft record, i.e. *@attr is a pointer into *@mrec.
187  *
188  * Return 0 on success, or -1 on error, with errno set to the error code.
189  *
190  * The read mft record is checked for having the magic FILE,
191  * and for having a matching sequence number (if MSEQNO(*@mref) != 0).
192  * If either of these fails, -1 is returned and errno is set to EIO. If you get
193  * this, but you still want to read the mft record (e.g. in order to correct
194  * it), use ntfs_mft_record_read() directly.
195  *
196  * Note: Caller has to free *@mrec when finished.
197  *
198  * Note: We do not check if the mft record is flagged in use. The caller can
199  *       check if desired.
200  */
201 int ntfs_file_record_read(const ntfs_volume *vol, const MFT_REF mref,
202                 MFT_RECORD **mrec, ATTR_RECORD **attr)
203 {
204         MFT_RECORD *m;
205         ATTR_RECORD *a;
206         int err;
207
208         if (!vol || !mrec) {
209                 errno = EINVAL;
210                 return -1;
211         }
212         m = *mrec;
213         if (!m) {
214                 m = (MFT_RECORD*)malloc(vol->mft_record_size);
215                 if (!m)
216                         return -1;
217         }
218         if (ntfs_mft_record_read(vol, mref, m)) {
219                 err = errno;
220                 goto read_failed;
221         }
222         if (!ntfs_is_file_record(m->magic))
223                 goto file_corrupt;
224         if (MSEQNO(mref) && MSEQNO(mref) != le16_to_cpu(m->sequence_number))
225                 goto file_corrupt;
226         a = (ATTR_RECORD*)((char*)m + le16_to_cpu(m->attrs_offset));
227         if (p2n(a) < p2n(m) || (char*)a > (char*)m + vol->mft_record_size)
228                 goto file_corrupt;
229         *mrec = m;
230         if (attr)
231                 *attr = a;
232         return 0;
233 file_corrupt:
234         Dputs("ntfs_file_record_read(): file is corrupt.");
235         err = EIO;
236 read_failed:
237         if (m != *mrec)
238                 free(m);
239         errno = err;
240         return -1;
241 }
242
243 /**
244  * ntfs_mft_record_alloc - allocate an mft record on an ntfs volume
245  * @vol:        mounted ntfs volume on which to allocate the mft record
246  * @start:      starting mft record at which to allocate (or -1 if none)
247  *
248  * Allocate an mft record in $MFT/$DATA starting to search for a free record
249  * at mft record number @start or at the current allocator position if
250  * @start_mref is -1, on the mounted ntfs volume @vol.
251  *
252  * On success return the now opened ntfs inode of the mft record.
253  *
254  * On error return NULL with errno set to the error code.
255  */
256 ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, u64 start)
257 {
258         if (!vol || !vol->mftbmp_na) {
259                 errno = EINVAL;
260                 return NULL;
261         }
262
263         errno = ENOTSUP;
264         return NULL;
265 }
266
267 /**
268  * ntfs_mft_record_free - free an mft record on an ntfs volume
269  * @vol:        mounted ntfs volume on which to free the mft record
270  * @ni:         open ntfs inode of the mft record to free
271  *
272  * Free the mft record of the open inode @ni on the mounted ntfs volume @vol.
273  * Note that this function calls ntfs_inode_close() internally and hence you
274  * cannot use the pointer @ni any more after this function returns success.
275  *
276  * On success return 0 and on error return -1 with errno set to the error code.
277  */
278 int ntfs_mft_record_free(ntfs_volume *vol, ntfs_inode *ni)
279 {
280         u64 mft_no;
281         u16 seq_no;
282
283         if (!vol || !vol->mftbmp_na || !ni) {
284                 errno = EINVAL;
285                 return -1;
286         }
287
288         /* Cache the mft reference for later. */
289         mft_no = ni->mft_no;
290
291         /* Mark the mft record as not in use. */
292         ni->mrec->flags &= ~MFT_RECORD_IN_USE;
293
294         /* Increment the sequence number, skipping zero, if it is not zero. */
295         seq_no = le16_to_cpu(ni->mrec->sequence_number);
296         if (seq_no == 0xffff)
297                 seq_no = 1;
298         else if (seq_no)
299                 seq_no++;
300         ni->mrec->sequence_number = cpu_to_le16(seq_no);
301
302         /* Set the inode dirty and close it so it is written out. */
303         ntfs_inode_mark_dirty(ni);
304         if (ntfs_inode_close(ni)) {
305                 int eo = errno;
306                 // FIXME: Eeek! We need rollback! (AIA)
307                 fprintf(stderr, "%s(): Eeek! Failed to close the inode."
308                                 "Leaving inconsistent metadata!\n",
309                                 __FUNCTION__);
310                 errno = eo;
311                 return -1;
312         }
313
314         /* Clear the bit in the $MFT/$BITMAP corresponding to this record. */
315         if (ntfs_bitmap_clear_run(vol->mftbmp_na, mft_no, 1)) {
316                 // FIXME: Eeek! We need rollback! (AIA)
317                 fprintf(stderr, "%s(): Eeek! Failed to clear the allocation "
318                                 "in the mft bitmap. Leaving deleted mft record "
319                                 "marked as in use in the mft bitmap and "
320                                 "pretending we succeeded. Error: %s\n",
321                                 __FUNCTION__, strerror(errno));
322         }
323         return 0;
324 }
325