1 /* ntfscomp.c - compression support for the NTFS filesystem */
3 * Copyright (C) 2007 Free Software Foundation, Inc.
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #include <grub/file.h>
21 #include <grub/misc.h>
22 #include <grub/disk.h>
24 #include <grub/ntfs.h>
26 GRUB_MOD_LICENSE ("GPLv3+");
29 decomp_nextvcn (struct grub_ntfs_comp
*cc
)
31 if (cc
->comp_head
>= cc
->comp_tail
)
32 return grub_error (GRUB_ERR_BAD_FS
, "compression block overflown");
35 (cc
->comp_table
[cc
->comp_head
].next_lcn
-
36 (cc
->comp_table
[cc
->comp_head
].next_vcn
- cc
->cbuf_vcn
)) << cc
->log_spc
,
38 1 << (cc
->log_spc
+ GRUB_NTFS_BLK_SHR
), cc
->cbuf
))
41 if ((cc
->cbuf_vcn
>= cc
->comp_table
[cc
->comp_head
].next_vcn
))
48 decomp_getch (struct grub_ntfs_comp
*cc
, grub_uint8_t
*res
)
50 if (cc
->cbuf_ofs
>= (1U << (cc
->log_spc
+ GRUB_NTFS_BLK_SHR
)))
52 if (decomp_nextvcn (cc
))
55 *res
= cc
->cbuf
[cc
->cbuf_ofs
++];
60 decomp_get16 (struct grub_ntfs_comp
*cc
, grub_uint16_t
* res
)
62 grub_uint8_t c1
= 0, c2
= 0;
64 if ((decomp_getch (cc
, &c1
)) || (decomp_getch (cc
, &c2
)))
66 *res
= ((grub_uint16_t
) c2
) * 256 + ((grub_uint16_t
) c1
);
70 /* Decompress a block (4096 bytes) */
72 decomp_block (struct grub_ntfs_comp
*cc
, grub_uint8_t
*dest
)
74 grub_uint16_t flg
, cnt
;
76 if (decomp_get16 (cc
, &flg
))
78 cnt
= (flg
& 0xFFF) + 1;
85 grub_uint32_t bits
, copied
;
87 bits
= copied
= tag
= 0;
90 if (copied
> GRUB_NTFS_COM_LEN
)
91 return grub_error (GRUB_ERR_BAD_FS
,
92 "compression block too large");
96 if (decomp_getch (cc
, &tag
))
106 grub_uint32_t i
, len
, delta
, code
, lmask
, dshift
;
109 if (decomp_get16 (cc
, &word
))
117 grub_error (GRUB_ERR_BAD_FS
, "nontext window empty");
121 for (i
= copied
- 1, lmask
= 0xFFF, dshift
= 12; i
>= 0x10;
128 delta
= code
>> dshift
;
129 len
= (code
& lmask
) + 3;
131 for (i
= 0; i
< len
; i
++)
133 dest
[copied
] = dest
[copied
- delta
- 1];
141 if (decomp_getch (cc
, &ch
))
153 if (cnt
!= GRUB_NTFS_COM_LEN
)
154 return grub_error (GRUB_ERR_BAD_FS
,
155 "invalid compression block size");
163 n
= (1 << (cc
->log_spc
+ GRUB_NTFS_BLK_SHR
)) - cc
->cbuf_ofs
;
168 grub_memcpy (dest
, &cc
->cbuf
[cc
->cbuf_ofs
], n
);
173 if ((cnt
) && (decomp_nextvcn (cc
)))
180 read_block (struct grub_ntfs_rlst
*ctx
, grub_uint8_t
*buf
, grub_size_t num
)
182 int log_cpb
= GRUB_NTFS_LOG_COM_SEC
- ctx
->comp
.log_spc
;
188 if ((ctx
->target_vcn
& 0xF) == 0)
191 if (ctx
->comp
.comp_head
!= ctx
->comp
.comp_tail
192 && !(ctx
->flags
& GRUB_NTFS_RF_BLNK
))
193 return grub_error (GRUB_ERR_BAD_FS
, "invalid compression block");
194 ctx
->comp
.comp_head
= ctx
->comp
.comp_tail
= 0;
195 ctx
->comp
.cbuf_vcn
= ctx
->target_vcn
;
196 ctx
->comp
.cbuf_ofs
= (1 << (ctx
->comp
.log_spc
+ GRUB_NTFS_BLK_SHR
));
197 if (ctx
->target_vcn
>= ctx
->next_vcn
)
199 if (grub_ntfs_read_run_list (ctx
))
202 while (ctx
->target_vcn
+ 16 > ctx
->next_vcn
)
204 if (ctx
->flags
& GRUB_NTFS_RF_BLNK
)
206 ctx
->comp
.comp_table
[ctx
->comp
.comp_tail
].next_vcn
= ctx
->next_vcn
;
207 ctx
->comp
.comp_table
[ctx
->comp
.comp_tail
].next_lcn
=
208 ctx
->curr_lcn
+ ctx
->next_vcn
- ctx
->curr_vcn
;
209 ctx
->comp
.comp_tail
++;
210 if (grub_ntfs_read_run_list (ctx
))
215 nn
= (16 - (unsigned) (ctx
->target_vcn
& 0xF)) >> log_cpb
;
220 if (ctx
->flags
& GRUB_NTFS_RF_BLNK
)
222 ctx
->target_vcn
+= nn
<< log_cpb
;
223 if (ctx
->comp
.comp_tail
== 0)
227 grub_memset (buf
, 0, nn
* GRUB_NTFS_COM_LEN
);
228 buf
+= nn
* GRUB_NTFS_COM_LEN
;
229 if (grub_file_progress_hook
&& ctx
->file
)
230 grub_file_progress_hook (0, 0, nn
* GRUB_NTFS_COM_LEN
,
238 if (decomp_block (&ctx
->comp
, buf
))
241 buf
+= GRUB_NTFS_COM_LEN
;
242 if (grub_file_progress_hook
&& ctx
->file
)
243 grub_file_progress_hook (0, 0, GRUB_NTFS_COM_LEN
,
252 while ((ctx
->comp
.comp_head
< ctx
->comp
.comp_tail
) && (nn
))
257 ctx
->comp
.comp_table
[ctx
->comp
.comp_head
].next_vcn
-
261 ctx
->target_vcn
+= tt
;
266 (ctx
->comp
.comp_table
[ctx
->comp
.comp_head
].next_lcn
-
267 (ctx
->comp
.comp_table
[ctx
->comp
.comp_head
].next_vcn
-
268 ctx
->target_vcn
)) << ctx
->comp
.log_spc
, 0,
269 tt
<< (ctx
->comp
.log_spc
+ GRUB_NTFS_BLK_SHR
), buf
))
271 if (grub_file_progress_hook
&& ctx
->file
)
272 grub_file_progress_hook (0, 0,
273 tt
<< (ctx
->comp
.log_spc
274 + GRUB_NTFS_BLK_SHR
),
276 buf
+= tt
<< (ctx
->comp
.log_spc
+ GRUB_NTFS_BLK_SHR
);
279 if (ctx
->target_vcn
>=
280 ctx
->comp
.comp_table
[ctx
->comp
.comp_head
].next_vcn
)
281 ctx
->comp
.comp_head
++;
289 (ctx
->target_vcn
- ctx
->curr_vcn
+
290 ctx
->curr_lcn
) << ctx
->comp
.log_spc
, 0,
291 nn
<< (ctx
->comp
.log_spc
+ GRUB_NTFS_BLK_SHR
), buf
))
293 buf
+= nn
<< (ctx
->comp
.log_spc
+ GRUB_NTFS_BLK_SHR
);
294 if (grub_file_progress_hook
&& ctx
->file
)
295 grub_file_progress_hook (0, 0,
296 nn
<< (ctx
->comp
.log_spc
297 + GRUB_NTFS_BLK_SHR
),
300 ctx
->target_vcn
+= nn
;
308 ntfscomp (grub_uint8_t
*dest
, grub_disk_addr_t ofs
,
309 grub_size_t len
, struct grub_ntfs_rlst
*ctx
)
312 grub_disk_addr_t vcn
;
316 if ((ofs
& (~(GRUB_NTFS_COM_LEN
- 1))) == ctx
->attr
->save_pos
)
320 n
= GRUB_NTFS_COM_LEN
- (ofs
- ctx
->attr
->save_pos
);
324 grub_memcpy (dest
, ctx
->attr
->sbuf
+ ofs
- ctx
->attr
->save_pos
, n
);
325 if (grub_file_progress_hook
&& ctx
->file
)
326 grub_file_progress_hook (0, 0, n
, ctx
->file
);
337 ctx
->attr
->sbuf
= grub_malloc (GRUB_NTFS_COM_LEN
);
338 if (ctx
->attr
->sbuf
== NULL
)
340 ctx
->attr
->save_pos
= 1;
343 vcn
= ctx
->target_vcn
= (ofs
>> GRUB_NTFS_COM_LOG_LEN
) * (GRUB_NTFS_COM_SEC
>> ctx
->comp
.log_spc
);
344 ctx
->target_vcn
&= ~0xFULL
;
345 while (ctx
->next_vcn
<= ctx
->target_vcn
)
347 if (grub_ntfs_read_run_list (ctx
))
351 ctx
->comp
.comp_head
= ctx
->comp
.comp_tail
= 0;
352 ctx
->comp
.cbuf
= grub_malloc (1 << (ctx
->comp
.log_spc
+ GRUB_NTFS_BLK_SHR
));
358 //ctx->comp.disk->read_hook = read_hook;
359 //ctx->comp.disk->read_hook_data = read_hook_data;
361 if ((vcn
> ctx
->target_vcn
) &&
363 (ctx
, NULL
, (vcn
- ctx
->target_vcn
) >> (GRUB_NTFS_LOG_COM_SEC
- ctx
->comp
.log_spc
))))
369 if (ofs
% GRUB_NTFS_COM_LEN
)
371 grub_uint32_t t
, n
, o
;
372 void *file
= ctx
->file
;
376 t
= ctx
->target_vcn
<< (ctx
->comp
.log_spc
+ GRUB_NTFS_BLK_SHR
);
377 if (read_block (ctx
, ctx
->attr
->sbuf
, 1))
385 ctx
->attr
->save_pos
= t
;
387 o
= ofs
% GRUB_NTFS_COM_LEN
;
388 n
= GRUB_NTFS_COM_LEN
- o
;
391 grub_memcpy (dest
, &ctx
->attr
->sbuf
[o
], n
);
392 if (grub_file_progress_hook
&& ctx
->file
)
393 grub_file_progress_hook (0, 0, n
, ctx
->file
);
400 if (read_block (ctx
, dest
, len
/ GRUB_NTFS_COM_LEN
))
406 dest
+= (len
/ GRUB_NTFS_COM_LEN
) * GRUB_NTFS_COM_LEN
;
407 len
= len
% GRUB_NTFS_COM_LEN
;
411 void *file
= ctx
->file
;
414 t
= ctx
->target_vcn
<< (ctx
->comp
.log_spc
+ GRUB_NTFS_BLK_SHR
);
415 if (read_block (ctx
, ctx
->attr
->sbuf
, 1))
421 ctx
->attr
->save_pos
= t
;
423 grub_memcpy (dest
, ctx
->attr
->sbuf
, len
);
424 if (grub_file_progress_hook
&& file
)
425 grub_file_progress_hook (0, 0, len
, file
);
429 //ctx->comp.disk->read_hook = 0;
431 grub_free (ctx
->comp
.cbuf
);
435 GRUB_MOD_INIT (ntfscomp
)
437 grub_ntfscomp_func
= ntfscomp
;
440 GRUB_MOD_FINI (ntfscomp
)
442 grub_ntfscomp_func
= NULL
;