Import 2.1.112pre1
[davej-history.git] / fs / ntfs / attr.c
blob7c6a64e0da8b5c96a30ff36a66e6487076918115
1 /*
2 * attr.c
4 * Copyright (C) 1996-1997 Martin von Löwis
5 * Copyright (C) 1996-1997 Régis Duchesne
6 */
8 #include "types.h"
9 #include "struct.h"
10 #include "attr.h"
12 #include <errno.h>
13 #include "macros.h"
14 #include "support.h"
15 #include "util.h"
16 #include "super.h"
17 #include "inode.h"
19 /* Look if an attribute already exists in the inode, and if not, create it */
20 static int
21 new_attr(ntfs_inode *ino,int type,void *name,int namelen,int *pos, int *found)
23 int do_insert=0;
24 int i;
26 for(i=0;i<ino->attr_count;i++)
28 int n=min(namelen,ino->attrs[i].namelen);
29 int s=ntfs_uni_strncmp(ino->attrs[i].name,name,n);
31 * We assume that each attribute can be uniquely
32 * identified by inode
33 * number, attribute type and attribute name.
35 if(ino->attrs[i].type==type && ino->attrs[i].namelen==namelen && !s){
36 *found=1;
37 *pos=i;
38 return 0;
40 /* attributes are ordered by type, then by name */
41 if(ino->attrs[i].type>type || (ino->attrs[i].type==type && s==1)){
42 do_insert=1;
43 break;
47 /* re-allocate space */
48 if(ino->attr_count % 8 ==0)
50 ntfs_attribute* old=ino->attrs;
51 ino->attrs = (ntfs_attribute*)ntfs_malloc((ino->attr_count+8)*
52 sizeof(ntfs_attribute));
53 if(old){
54 ntfs_memcpy(ino->attrs,old,ino->attr_count*sizeof(ntfs_attribute));
55 ntfs_free(old);
58 if(do_insert)
59 ntfs_memmove(ino->attrs+i+1,ino->attrs+i,(ino->attr_count-i)*
60 sizeof(ntfs_attribute));
61 ino->attr_count++;
62 ino->attrs[i].type=type;
63 ino->attrs[i].namelen=namelen;
64 ino->attrs[i].name=name;
65 *pos=i;
66 *found=0;
67 return 0;
70 int
71 ntfs_make_attr_resident(ntfs_inode *ino,ntfs_attribute *attr)
73 int size=attr->size;
74 if(size>0){
75 /* FIXME: read data, free clusters */
76 return EOPNOTSUPP;
78 attr->resident=1;
79 return 0;
82 /* Store in the inode readable information about a run */
83 static void
84 ntfs_insert_run(ntfs_attribute *attr,int cnum,int cluster,int len)
86 /* (re-)allocate space if necessary */
87 if(attr->d.r.len % 8 == 0) {
88 ntfs_runlist* old;
89 old=attr->d.r.runlist;
90 attr->d.r.runlist=ntfs_malloc((attr->d.r.len+8)*sizeof(ntfs_runlist));
91 if(old) {
92 ntfs_memcpy(attr->d.r.runlist,old,attr->d.r.len
93 *sizeof(ntfs_runlist));
94 ntfs_free(old);
97 if(attr->d.r.len>cnum)
98 ntfs_memmove(attr->d.r.runlist+cnum+1,attr->d.r.runlist+cnum,
99 (attr->d.r.len-cnum)*sizeof(ntfs_runlist));
100 attr->d.r.runlist[cnum].cluster=cluster;
101 attr->d.r.runlist[cnum].len=len;
102 attr->d.r.len++;
105 int ntfs_extend_attr(ntfs_inode *ino, ntfs_attribute *attr, int *len,
106 int flags)
108 int error=0;
109 ntfs_runlist *rl;
110 int rlen,cluster;
111 int clen;
112 if(attr->compressed)return EOPNOTSUPP;
113 if(attr->resident)return EOPNOTSUPP;
114 if(ino->record_count>1)return EOPNOTSUPP;
115 rl=attr->d.r.runlist;
116 rlen=attr->d.r.len-1;
117 if(rlen>=0)
118 cluster=rl[rlen].cluster+rl[rlen].len;
119 else
120 /* no preference for allocation space */
121 cluster=0;
122 /* round up to multiple of cluster size */
123 clen=(*len+ino->vol->clustersize-1)/ino->vol->clustersize;
124 /* FIXME: try to allocate smaller pieces */
125 error=ntfs_allocate_clusters(ino->vol,&cluster,&clen,
126 flags|ALLOC_REQUIRE_SIZE);
127 if(error)return error;
128 attr->allocated+=clen;
129 *len=clen*ino->vol->clustersize;
130 /* contiguous chunk */
131 if(rlen>=0 && cluster==rl[rlen].cluster+rl[rlen].len){
132 rl[rlen].len+=clen;
133 return 0;
135 ntfs_insert_run(attr,rlen+1,cluster,*len);
136 return 0;
140 ntfs_make_attr_nonresident(ntfs_inode *ino, ntfs_attribute *attr)
142 void *data=attr->d.data;
143 int len=attr->size;
144 int error,alen;
145 ntfs_io io;
146 attr->d.r.len=0;
147 attr->d.r.runlist=0;
148 attr->resident=0;
149 attr->allocated=attr->initialized=0;
150 alen=len;
151 error=ntfs_extend_attr(ino,attr,&alen,ALLOC_REQUIRE_SIZE);
152 if(error)return error;/* FIXME: On error, restore old values */
153 io.fn_put=ntfs_put;
154 io.fn_get=ntfs_get;
155 io.param=data;
156 io.size=len;
157 io.do_read=0;
158 return ntfs_readwrite_attr(ino,attr,0,&io);
161 /* Resize the attribute to a newsize */
162 int ntfs_resize_attr(ntfs_inode *ino, ntfs_attribute *attr, int newsize)
164 int error=0;
165 int oldsize=attr->size;
166 int clustersize=ino->vol->clustersize;
167 int i,count,newlen,newcount;
168 ntfs_runlist *rl;
170 if(newsize==oldsize)
171 return 0;
172 /* modifying compressed attributes not supported yet */
173 if(attr->compressed)
174 /* extending is easy: just insert sparse runs */
175 return EOPNOTSUPP;
176 if(attr->resident){
177 void *v;
178 if(newsize>ino->vol->clustersize){
179 error=ntfs_make_attr_nonresident(ino,attr);
180 if(error)return error;
181 return ntfs_resize_attr(ino,attr,newsize);
183 v=attr->d.data;
184 if(newsize){
185 attr->d.data=ntfs_malloc(newsize);
186 if(!attr->d.data)
187 return ENOMEM;
188 ntfs_bzero(attr->d.data+oldsize,newsize);
189 ntfs_memcpy(attr->d.data,v,min(newsize,oldsize));
190 }else
191 attr->d.data=0;
192 ntfs_free(v);
193 attr->size=newsize;
194 return 0;
196 /* non-resident attribute */
197 rl=attr->d.r.runlist;
198 if(newsize<oldsize){
199 for(i=0,count=0;i<attr->d.r.len;i++){
200 if((count+rl[i].len)*clustersize>newsize)
201 break;
202 count+=rl[i].len;
204 newlen=i+1;
205 /* free unused clusters in current run, unless sparse */
206 newcount=count;
207 if(rl[i].cluster!=-1){
208 int rounded=newsize-count*clustersize;
209 rounded=(rounded+clustersize-1)/clustersize;
210 error=ntfs_deallocate_clusters(ino->vol,rl[i].cluster+rounded,
211 rl[i].len-rounded);
212 if(error)
213 return error; /* FIXME: incomplete operation */
214 rl[i].len=rounded;
215 newcount=count+rounded;
217 /* free all other runs */
218 for(i++;i<attr->d.r.len;i++)
219 if(rl[i].cluster!=-1){
220 error=ntfs_deallocate_clusters(ino->vol,rl[i].cluster,rl[i].len);
221 if(error)
222 return error; /* FIXME: incomplete operation */
224 /* FIXME? free space for extra runs in memory */
225 attr->d.r.len=newlen;
226 }else{
227 newlen=newsize;
228 error=ntfs_extend_attr(ino,attr,&newlen,ALLOC_REQUIRE_SIZE);
229 if(error)return error; /* FIXME: incomplete */
230 newcount=newlen/clustersize;
232 /* fill in new sizes */
233 attr->allocated = newcount*clustersize;
234 attr->size = newsize;
235 attr->initialized = newsize;
236 if(!newsize)
237 error=ntfs_make_attr_resident(ino,attr);
238 return error;
241 int ntfs_create_attr(ntfs_inode *ino, int anum, char *aname, void *data,
242 int dsize, ntfs_attribute **rattr)
244 void *name;
245 int namelen;
246 int found,i;
247 int error;
248 ntfs_attribute *attr;
249 if(dsize>ino->vol->mft_recordsize)
250 /* FIXME: non-resident attributes */
251 return EOPNOTSUPP;
252 if(aname){
253 namelen=strlen(aname);
254 name=ntfs_malloc(2*namelen);
255 ntfs_ascii2uni(name,aname,namelen);
256 }else{
257 name=0;
258 namelen=0;
260 new_attr(ino,anum,name,namelen,&i,&found);
261 if(found){
262 ntfs_free(name);
263 return EEXIST;
265 *rattr=attr=ino->attrs+i;
266 /* allocate a new number.
267 FIXME: Should this happen on inode writeback?
268 FIXME: extensions records not supported */
269 error=ntfs_allocate_attr_number(ino,&i);
270 if(error)
271 return error;
272 attr->attrno=i;
274 attr->resident=1;
275 attr->compressed=attr->cengine=0;
276 attr->size=attr->allocated=attr->initialized=dsize;
278 /* FIXME: INDEXED information should come from $AttrDef
279 Currently, only file names are indexed */
280 if(anum==ino->vol->at_file_name){
281 attr->indexed=1;
282 }else
283 attr->indexed=0;
284 attr->d.data=ntfs_malloc(dsize);
285 ntfs_memcpy(attr->d.data,data,dsize);
286 return 0;
289 /* Non-resident attributes are stored in runs (intervals of clusters).
291 * This function stores in the inode readable information about a non-resident
292 * attribute.
294 static int
295 ntfs_process_runs(ntfs_inode *ino,ntfs_attribute* attr,unsigned char *data)
297 int startvcn,endvcn;
298 int vcn,cnum;
299 int cluster,len,ctype;
300 startvcn = NTFS_GETU64(data+0x10);
301 endvcn = NTFS_GETU64(data+0x18);
303 /* check whether this chunk really belongs to the end */
304 for(cnum=0,vcn=0;cnum<attr->d.r.len;cnum++)
305 vcn+=attr->d.r.runlist[cnum].len;
306 if(vcn!=startvcn)
308 ntfs_error("Problem with runlist in extended record\n");
309 return -1;
311 if(!endvcn)
313 endvcn = NTFS_GETU64(data+0x28)-1; /* allocated length */
314 endvcn /= ino->vol->clustersize;
316 data=data+NTFS_GETU16(data+0x20);
317 cnum=attr->d.r.len;
318 cluster=0;
319 for(vcn=startvcn; vcn<=endvcn; vcn+=len)
321 if(ntfs_decompress_run(&data,&len,&cluster,&ctype))
322 return -1;
323 if(ctype)
324 ntfs_insert_run(attr,cnum,-1,len);
325 else
326 ntfs_insert_run(attr,cnum,cluster,len);
327 cnum++;
329 return 0;
332 /* Insert the attribute starting at attr in the inode ino */
333 int ntfs_insert_attribute(ntfs_inode *ino, unsigned char* attrdata)
335 int i,found;
336 int type;
337 short int *name;
338 int namelen;
339 void *data;
340 ntfs_attribute *attr;
342 type = NTFS_GETU32(attrdata);
343 namelen = NTFS_GETU8(attrdata+9);
344 /* read the attribute's name if it has one */
345 if(!namelen)
346 name=0;
347 else
349 /* 1 Unicode character fits in 2 bytes */
350 name=ntfs_malloc(2*namelen);
351 ntfs_memcpy(name,attrdata+NTFS_GETU16(attrdata+10),2*namelen);
353 new_attr(ino,type,name,namelen,&i,&found);
354 /* We can have in one inode two attributes with type 0x00000030 (File Name)
355 and without name */
356 if(found && /*FIXME*/type!=ino->vol->at_file_name)
358 ntfs_process_runs(ino,ino->attrs+i,attrdata);
359 return 0;
361 attr=ino->attrs+i;
362 attr->resident=NTFS_GETU8(attrdata+8)==0;
363 attr->compressed=NTFS_GETU16(attrdata+0xC);
364 attr->attrno=NTFS_GETU16(attrdata+0xE);
366 if(attr->resident) {
367 attr->size=NTFS_GETU16(attrdata+0x10);
368 data=attrdata+NTFS_GETU16(attrdata+0x14);
369 attr->d.data = (void*)ntfs_malloc(attr->size);
370 ntfs_memcpy(attr->d.data,data,attr->size);
371 attr->indexed=NTFS_GETU16(attrdata+0x16);
372 }else{
373 attr->allocated=NTFS_GETU32(attrdata+0x28);
374 attr->size=NTFS_GETU32(attrdata+0x30);
375 attr->initialized=NTFS_GETU32(attrdata+0x38);
376 attr->cengine=NTFS_GETU16(attrdata+0x22);
377 if(attr->compressed)
378 attr->compsize=NTFS_GETU32(attrdata+0x40);
379 ino->attrs[i].d.r.runlist=0;
380 ino->attrs[i].d.r.len=0;
381 ntfs_process_runs(ino,attr,attrdata);
383 return 0;
386 /* process compressed attributes */
387 int ntfs_read_compressed(ntfs_inode *ino, ntfs_attribute *attr, int offset,
388 ntfs_io *dest)
390 int error=0;
391 int clustersize,l;
392 int s_vcn,rnum,vcn,cluster,len,chunk,got,cl1,l1,offs1,copied;
393 char *comp=0,*comp1;
394 char *decomp=0;
395 ntfs_io io;
396 ntfs_runlist *rl;
398 l=dest->size;
399 clustersize=ino->vol->clustersize;
400 /* starting cluster of potential chunk
401 there are three situations:
402 a) in a large uncompressible or sparse chunk,
403 s_vcn is in the middle of a run
404 b) s_vcn is right on a run border
405 c) when several runs make a chunk, s_vcn is before the chunks
407 s_vcn=offset/clustersize;
408 /* round down to multiple of 16 */
409 s_vcn &= ~15;
410 rl=attr->d.r.runlist;
411 for(rnum=vcn=0;rnum<attr->d.r.len && vcn+rl->len<=s_vcn;rnum++,rl++)
412 vcn+=rl->len;
413 if(rnum==attr->d.r.len){
414 /* beyond end of file */
415 /* FIXME: check allocated/initialized */
416 dest->size=0;
417 return 0;
419 io.do_read=1;
420 io.fn_put=ntfs_put;
421 io.fn_get=0;
422 cluster=rl->cluster;
423 len=rl->len;
424 copied=0;
425 while(l){
426 chunk=0;
427 if(cluster==-1){
428 /* sparse cluster */
429 char *sparse=ntfs_calloc(512);
430 int l1;
431 if(!sparse)return ENOMEM;
432 if((len-(s_vcn-vcn)) & 15)
433 ntfs_error("unexpected sparse chunk size");
434 l1=chunk = min((vcn+len)*clustersize-offset,l);
435 while(l1){
436 int i=min(l1,512);
437 dest->fn_put(dest,sparse,i);
438 l1-=i;
440 ntfs_free(sparse);
441 }else if(dest->do_read){
442 if(!comp){
443 comp=ntfs_malloc(16*clustersize);
444 if(!comp){
445 error=ENOMEM;
446 goto out;
449 got=0;
450 /* we might need to start in the middle of a run */
451 cl1=cluster+s_vcn-vcn;
452 comp1=comp;
454 io.param=comp1;
455 l1=min(len-max(s_vcn-vcn,0),16-got);
456 io.size=l1*clustersize;
457 error=ntfs_getput_clusters(ino->vol,cl1,0,&io);
458 if(error)goto out;
459 if(l1+max(s_vcn-vcn,0)==len){
460 rnum++;rl++;
461 vcn+=len;
462 cluster=cl1=rl->cluster;
463 len=rl->len;
465 got+=l1;
466 comp1+=l1*clustersize;
467 }while(cluster!=-1 && got<16); /* until empty run */
468 chunk=16*clustersize;
469 if(cluster!=-1 || got==16)
470 /* uncompressible */
471 comp1=comp;
472 else{
473 if(!decomp){
474 decomp=ntfs_malloc(16*clustersize);
475 if(!decomp){
476 error=ENOMEM;
477 goto out;
480 /* make sure there are null bytes
481 after the last block */
482 *(ntfs_u32*)comp1=0;
483 ntfs_decompress(decomp,comp,chunk);
484 comp1=decomp;
486 offs1=offset-s_vcn*clustersize;
487 chunk=min(16*clustersize-offs1,chunk);
488 chunk=min(l,chunk);
489 dest->fn_put(dest,comp1+offs1,chunk);
491 l-=chunk;
492 copied+=chunk;
493 offset+=chunk;
494 s_vcn=offset/clustersize & ~15;
495 if(l && offset>=((vcn+len)*clustersize)){
496 rnum++;rl++;
497 vcn+=len;
498 cluster=rl->cluster;
499 len=rl->len;
502 out:
503 if(comp)ntfs_free(comp);
504 if(decomp)ntfs_free(decomp);
505 dest->size=copied;
506 return error;
509 int ntfs_write_compressed(ntfs_inode *ino, ntfs_attribute *attr, int offset,
510 ntfs_io *dest)
512 return EOPNOTSUPP;
516 * Local variables:
517 * c-file-style: "linux"
518 * End: