source: grub-pc/trunk/fuentes/grub-core/fs/ntfscomp.c @ 22

Last change on this file since 22 was 22, checked in by mabarracus, 4 years ago

updated version and apply net.ifnames=0 into debian/rules

File size: 10.2 KB
Line 
1/* ntfscomp.c - compression support for the NTFS filesystem */
2/*
3 *  Copyright (C) 2007 Free Software Foundation, Inc.
4 *
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.
9 *
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.
14 *
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/>.
17 */
18
19#include <grub/file.h>
20#include <grub/mm.h>
21#include <grub/misc.h>
22#include <grub/disk.h>
23#include <grub/dl.h>
24#include <grub/ntfs.h>
25
26GRUB_MOD_LICENSE ("GPLv3+");
27
28static grub_err_t
29decomp_nextvcn (struct grub_ntfs_comp *cc)
30{
31  if (cc->comp_head >= cc->comp_tail)
32    return grub_error (GRUB_ERR_BAD_FS, "compression block overflown");
33  if (grub_disk_read
34      (cc->disk,
35       (cc->comp_table[cc->comp_head].next_lcn -
36        (cc->comp_table[cc->comp_head].next_vcn - cc->cbuf_vcn)) << cc->log_spc,
37       0,
38       1 << (cc->log_spc + GRUB_NTFS_BLK_SHR), cc->cbuf))
39    return grub_errno;
40  cc->cbuf_vcn++;
41  if ((cc->cbuf_vcn >= cc->comp_table[cc->comp_head].next_vcn))
42    cc->comp_head++;
43  cc->cbuf_ofs = 0;
44  return 0;
45}
46
47static grub_err_t
48decomp_getch (struct grub_ntfs_comp *cc, grub_uint8_t *res)
49{
50  if (cc->cbuf_ofs >= (1U << (cc->log_spc + GRUB_NTFS_BLK_SHR)))
51    {
52      if (decomp_nextvcn (cc))
53        return grub_errno;
54    }
55  *res = cc->cbuf[cc->cbuf_ofs++];
56  return 0;
57}
58
59static grub_err_t
60decomp_get16 (struct grub_ntfs_comp *cc, grub_uint16_t * res)
61{
62  grub_uint8_t c1 = 0, c2 = 0;
63
64  if ((decomp_getch (cc, &c1)) || (decomp_getch (cc, &c2)))
65    return grub_errno;
66  *res = ((grub_uint16_t) c2) * 256 + ((grub_uint16_t) c1);
67  return 0;
68}
69
70/* Decompress a block (4096 bytes) */
71static grub_err_t
72decomp_block (struct grub_ntfs_comp *cc, grub_uint8_t *dest)
73{
74  grub_uint16_t flg, cnt;
75
76  if (decomp_get16 (cc, &flg))
77    return grub_errno;
78  cnt = (flg & 0xFFF) + 1;
79
80  if (dest)
81    {
82      if (flg & 0x8000)
83        {
84          grub_uint8_t tag;
85          grub_uint32_t bits, copied;
86
87          bits = copied = tag = 0;
88          while (cnt > 0)
89            {
90              if (copied > GRUB_NTFS_COM_LEN)
91                return grub_error (GRUB_ERR_BAD_FS,
92                                   "compression block too large");
93
94              if (!bits)
95                {
96                  if (decomp_getch (cc, &tag))
97                    return grub_errno;
98
99                  bits = 8;
100                  cnt--;
101                  if (cnt <= 0)
102                    break;
103                }
104              if (tag & 1)
105                {
106                  grub_uint32_t i, len, delta, code, lmask, dshift;
107                  grub_uint16_t word;
108
109                  if (decomp_get16 (cc, &word))
110                    return grub_errno;
111
112                  code = word;
113                  cnt -= 2;
114
115                  if (!copied)
116                    {
117                      grub_error (GRUB_ERR_BAD_FS, "nontext window empty");
118                      return 0;
119                    }
120
121                  for (i = copied - 1, lmask = 0xFFF, dshift = 12; i >= 0x10;
122                       i >>= 1)
123                    {
124                      lmask >>= 1;
125                      dshift--;
126                    }
127
128                  delta = code >> dshift;
129                  len = (code & lmask) + 3;
130
131                  for (i = 0; i < len; i++)
132                    {
133                      dest[copied] = dest[copied - delta - 1];
134                      copied++;
135                    }
136                }
137              else
138                {
139                  grub_uint8_t ch = 0;
140
141                  if (decomp_getch (cc, &ch))
142                    return grub_errno;
143                  dest[copied++] = ch;
144                  cnt--;
145                }
146              tag >>= 1;
147              bits--;
148            }
149          return 0;
150        }
151      else
152        {
153          if (cnt != GRUB_NTFS_COM_LEN)
154            return grub_error (GRUB_ERR_BAD_FS,
155                               "invalid compression block size");
156        }
157    }
158
159  while (cnt > 0)
160    {
161      int n;
162
163      n = (1 << (cc->log_spc + GRUB_NTFS_BLK_SHR)) - cc->cbuf_ofs;
164      if (n > cnt)
165        n = cnt;
166      if ((dest) && (n))
167        {
168          grub_memcpy (dest, &cc->cbuf[cc->cbuf_ofs], n);
169          dest += n;
170        }
171      cnt -= n;
172      cc->cbuf_ofs += n;
173      if ((cnt) && (decomp_nextvcn (cc)))
174        return grub_errno;
175    }
176  return 0;
177}
178
179static grub_err_t
180read_block (struct grub_ntfs_rlst *ctx, grub_uint8_t *buf, grub_size_t num)
181{
182  int log_cpb = GRUB_NTFS_LOG_COM_SEC - ctx->comp.log_spc;
183
184  while (num)
185    {
186      grub_size_t nn;
187
188      if ((ctx->target_vcn & 0xF) == 0)
189        {
190
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)
198            {
199              if (grub_ntfs_read_run_list (ctx))
200                return grub_errno;
201            }
202          while (ctx->target_vcn + 16 > ctx->next_vcn)
203            {
204              if (ctx->flags & GRUB_NTFS_RF_BLNK)
205                break;
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))
211                return grub_errno;
212            }
213        }
214
215      nn = (16 - (unsigned) (ctx->target_vcn & 0xF)) >> log_cpb;
216      if (nn > num)
217        nn = num;
218      num -= nn;
219
220      if (ctx->flags & GRUB_NTFS_RF_BLNK)
221        {
222          ctx->target_vcn += nn << log_cpb;
223          if (ctx->comp.comp_tail == 0)
224            {
225              if (buf)
226                {
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,
231                                             ctx->file);
232                }
233            }
234          else
235            {
236              while (nn)
237                {
238                  if (decomp_block (&ctx->comp, buf))
239                    return grub_errno;
240                  if (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,
244                                             ctx->file);
245                  nn--;
246                }
247            }
248        }
249      else
250        {
251          nn <<= log_cpb;
252          while ((ctx->comp.comp_head < ctx->comp.comp_tail) && (nn))
253            {
254              grub_disk_addr_t tt;
255
256              tt =
257                ctx->comp.comp_table[ctx->comp.comp_head].next_vcn -
258                ctx->target_vcn;
259              if (tt > nn)
260                tt = nn;
261              ctx->target_vcn += tt;
262              if (buf)
263                {
264                  if (grub_disk_read
265                      (ctx->comp.disk,
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))
270                    return grub_errno;
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),
275                                             ctx->file);
276                  buf += tt << (ctx->comp.log_spc + GRUB_NTFS_BLK_SHR);
277                }
278              nn -= tt;
279              if (ctx->target_vcn >=
280                  ctx->comp.comp_table[ctx->comp.comp_head].next_vcn)
281                ctx->comp.comp_head++;
282            }
283          if (nn)
284            {
285              if (buf)
286                {
287                  if (grub_disk_read
288                      (ctx->comp.disk,
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))
292                    return grub_errno;
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),
298                                             ctx->file);
299                }
300              ctx->target_vcn += nn;
301            }
302        }
303    }
304  return 0;
305}
306
307static grub_err_t
308ntfscomp (grub_uint8_t *dest, grub_disk_addr_t ofs,
309          grub_size_t len, struct grub_ntfs_rlst *ctx)
310{
311  grub_err_t ret;
312  grub_disk_addr_t vcn;
313
314  if (ctx->attr->sbuf)
315    {
316      if ((ofs & (~(GRUB_NTFS_COM_LEN - 1))) == ctx->attr->save_pos)
317        {
318          grub_disk_addr_t n;
319
320          n = GRUB_NTFS_COM_LEN - (ofs - ctx->attr->save_pos);
321          if (n > len)
322            n = len;
323
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);
327          if (n == len)
328            return 0;
329
330          dest += n;
331          len -= n;
332          ofs += n;
333        }
334    }
335  else
336    {
337      ctx->attr->sbuf = grub_malloc (GRUB_NTFS_COM_LEN);
338      if (ctx->attr->sbuf == NULL)
339        return grub_errno;
340      ctx->attr->save_pos = 1;
341    }
342
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)
346    {
347      if (grub_ntfs_read_run_list (ctx))
348        return grub_errno;
349    }
350
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));
353  if (!ctx->comp.cbuf)
354    return 0;
355
356  ret = 0;
357
358  //ctx->comp.disk->read_hook = read_hook;
359  //ctx->comp.disk->read_hook_data = read_hook_data;
360
361  if ((vcn > ctx->target_vcn) &&
362      (read_block
363       (ctx, NULL, (vcn - ctx->target_vcn) >> (GRUB_NTFS_LOG_COM_SEC - ctx->comp.log_spc))))
364    {
365      ret = grub_errno;
366      goto quit;
367    }
368
369  if (ofs % GRUB_NTFS_COM_LEN)
370    {
371      grub_uint32_t t, n, o;
372      void *file = ctx->file;
373
374      ctx->file = 0;
375
376      t = ctx->target_vcn << (ctx->comp.log_spc + GRUB_NTFS_BLK_SHR);
377      if (read_block (ctx, ctx->attr->sbuf, 1))
378        {
379          ret = grub_errno;
380          goto quit;
381        }
382
383      ctx->file = file;
384
385      ctx->attr->save_pos = t;
386
387      o = ofs % GRUB_NTFS_COM_LEN;
388      n = GRUB_NTFS_COM_LEN - o;
389      if (n > len)
390        n = len;
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);
394      if (n == len)
395        goto quit;
396      dest += n;
397      len -= n;
398    }
399
400  if (read_block (ctx, dest, len / GRUB_NTFS_COM_LEN))
401    {
402      ret = grub_errno;
403      goto quit;
404    }
405
406  dest += (len / GRUB_NTFS_COM_LEN) * GRUB_NTFS_COM_LEN;
407  len = len % GRUB_NTFS_COM_LEN;
408  if (len)
409    {
410      grub_uint32_t t;
411      void *file = ctx->file;
412
413      ctx->file = 0;
414      t = ctx->target_vcn << (ctx->comp.log_spc + GRUB_NTFS_BLK_SHR);
415      if (read_block (ctx, ctx->attr->sbuf, 1))
416        {
417          ret = grub_errno;
418          goto quit;
419        }
420
421      ctx->attr->save_pos = t;
422
423      grub_memcpy (dest, ctx->attr->sbuf, len);
424      if (grub_file_progress_hook && file)
425        grub_file_progress_hook (0, 0, len, file);
426    }
427
428quit:
429  //ctx->comp.disk->read_hook = 0;
430  if (ctx->comp.cbuf)
431    grub_free (ctx->comp.cbuf);
432  return ret;
433}
434
435GRUB_MOD_INIT (ntfscomp)
436{
437  grub_ntfscomp_func = ntfscomp;
438}
439
440GRUB_MOD_FINI (ntfscomp)
441{
442  grub_ntfscomp_func = NULL;
443}
Note: See TracBrowser for help on using the repository browser.