2 * efs.c - Limited processing of encrypted files
4 * This module is part of ntfs-3g library
6 * Copyright (c) 2009 Martin Bene
7 * Copyright (c) 2009-2010 Jean-Pierre Andre
9 * This program/include file is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as published
11 * by the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program/include file is distributed in the hope that it will be
15 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
16 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program (in the main directory of the NTFS-3G
21 * distribution in the file COPYING); if not, write to the Free Software
22 * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
38 #ifdef HAVE_SYS_STAT_H
42 #ifdef HAVE_SYS_SYSMACROS_H
43 #include <sys/sysmacros.h>
58 static ntfschar logged_utility_stream_name
[] = {
59 const_cpu_to_le16('$'),
60 const_cpu_to_le16('E'),
61 const_cpu_to_le16('F'),
62 const_cpu_to_le16('S'),
68 * Get the ntfs EFS info into an extended attribute
71 int ntfs_get_efs_info(ntfs_inode
*ni
, char *value
, size_t size
)
73 EFS_ATTR_HEADER
*efs_info
;
77 if (ni
->flags
& FILE_ATTR_ENCRYPTED
) {
78 efs_info
= (EFS_ATTR_HEADER
*)ntfs_attr_readall(ni
,
79 AT_LOGGED_UTILITY_STREAM
,(ntfschar
*)NULL
, 0,
82 && (le32_to_cpu(efs_info
->length
) == attr_size
)) {
83 if (attr_size
<= (s64
)size
) {
85 memcpy(value
,efs_info
,attr_size
);
99 ntfs_log_error("Bad efs_info for inode %lld\n",
100 (long long)ni
->mft_no
);
102 ntfs_log_error("Could not get efsinfo"
104 (long long)ni
->mft_no
);
111 ntfs_log_trace("Inode %lld is not encrypted\n",
112 (long long)ni
->mft_no
);
115 return (attr_size
? (int)attr_size
: -errno
);
119 * Fix all encrypted AT_DATA attributes of an inode
121 * The fix may require making an attribute non resident, which
122 * requires more space in the MFT record, and may cause some
123 * attribute to be expelled and the full record to be reorganized.
124 * When this happens, the search for data attributes has to be
127 * Returns zero if successful.
128 * -1 if there is a problem.
131 static int fixup_loop(ntfs_inode
*ni
)
133 ntfs_attr_search_ctx
*ctx
;
144 ctx
= ntfs_attr_get_search_ctx(ni
, NULL
);
146 ntfs_log_error("Failed to get ctx for efs\n");
150 while (!restart
&& !res
151 && !ntfs_attr_lookup(AT_DATA
, NULL
, 0,
152 CASE_SENSITIVE
, 0, NULL
, 0, ctx
)) {
155 na
= ntfs_attr_open(ctx
->ntfs_ino
, AT_DATA
,
156 (ntfschar
*)((u8
*)a
+ le16_to_cpu(a
->name_offset
)),
159 ntfs_log_error("can't open DATA Attribute\n");
162 if (na
&& !(ctx
->attr
->flags
& ATTR_IS_ENCRYPTED
)) {
163 if (!NAttrNonResident(na
)
164 && ntfs_attr_make_non_resident(na
, ctx
)) {
166 * ntfs_attr_make_non_resident fails if there
167 * is not enough space in the MFT record.
168 * When this happens, force making non-resident
169 * so that some other attribute is expelled.
171 if (ntfs_attr_force_non_resident(na
)) {
174 /* make sure there is some progress */
177 ntfs_log_error("Multiple failure"
178 " making non resident\n");
181 ntfs_attr_put_search_ctx(ctx
);
182 ctx
= (ntfs_attr_search_ctx
*)NULL
;
189 && ntfs_efs_fixup_attribute(ctx
, na
)) {
190 ntfs_log_error("Error in efs fixup of AT_DATA Attribute\n");
197 } while (restart
&& !res
);
199 ntfs_attr_put_search_ctx(ctx
);
204 * Set the efs data from an extended attribute
205 * Warning : the new data is not checked
206 * Returns 0, or -1 if there is a problem
209 int ntfs_set_efs_info(ntfs_inode
*ni
, const char *value
, size_t size
,
216 const EFS_ATTR_HEADER
*info_header
;
219 if (ni
&& value
&& size
) {
220 if (ni
->flags
& (FILE_ATTR_ENCRYPTED
| FILE_ATTR_COMPRESSED
)) {
221 if (ni
->flags
& FILE_ATTR_ENCRYPTED
) {
222 ntfs_log_trace("Inode %lld already encrypted\n",
223 (long long)ni
->mft_no
);
227 * Possible problem : if encrypted file was
228 * restored in a compressed directory, it was
229 * restored as compressed.
230 * TODO : decompress first.
232 ntfs_log_error("Inode %lld cannot be encrypted and compressed\n",
233 (long long)ni
->mft_no
);
238 info_header
= (const EFS_ATTR_HEADER
*)value
;
239 /* make sure we get a likely efsinfo */
240 if (le32_to_cpu(info_header
->length
) != size
) {
244 if (!ntfs_attr_exist(ni
,AT_LOGGED_UTILITY_STREAM
,
245 (ntfschar
*)NULL
,0)) {
246 if (!(flags
& XATTR_REPLACE
)) {
248 * no logged_utility_stream attribute : add one,
249 * apparently, this does not feed the new value in
251 res
= ntfs_attr_add(ni
,AT_LOGGED_UTILITY_STREAM
,
252 logged_utility_stream_name
,4,
253 (u8
*)NULL
,(s64
)size
);
264 * open and update the existing efs data
266 na
= ntfs_attr_open(ni
, AT_LOGGED_UTILITY_STREAM
,
267 logged_utility_stream_name
, 4);
269 /* resize attribute */
270 res
= ntfs_attr_truncate(na
, (s64
)size
);
271 /* overwrite value if any */
273 written
= (int)ntfs_attr_pwrite(na
,
274 (s64
)0, (s64
)size
, value
);
275 if (written
!= (s64
)size
) {
276 ntfs_log_error("Failed to "
277 "update efs data\n");
287 /* Don't handle AT_DATA Attribute(s) if inode is a directory */
288 if (!(ni
->mrec
->flags
& MFT_RECORD_IS_DIRECTORY
)) {
289 /* iterate over AT_DATA attributes */
290 /* set encrypted flag, truncate attribute to match padding bytes */
295 ni
->flags
|= FILE_ATTR_ENCRYPTED
;
297 NInoFileNameSetDirty(ni
);
303 return (res
? -1 : 0);
307 * Fixup raw encrypted AT_DATA Attribute
308 * read padding length from last two bytes
309 * truncate attribute, make non-resident,
310 * set data size to match padding length
311 * set ATTR_IS_ENCRYPTED flag on attribute
313 * Return 0 if successful
314 * -1 if failed (errno tells why)
317 int ntfs_efs_fixup_attribute(ntfs_attr_search_ctx
*ctx
, ntfs_attr
*na
)
324 BOOL close_ctx
= FALSE
;
327 ntfs_log_error("no na specified for efs_fixup_attribute\n");
331 ctx
= ntfs_attr_get_search_ctx(na
->ni
, NULL
);
333 ntfs_log_error("Failed to get ctx for efs\n");
337 if (ntfs_attr_lookup(AT_DATA
, na
->name
, na
->name_len
,
338 CASE_SENSITIVE
, 0, NULL
, 0, ctx
)) {
339 ntfs_log_error("attr lookup for AT_DATA attribute failed in efs fixup\n");
343 if (!NAttrNonResident(na
)) {
344 ntfs_log_error("Cannot make non resident"
345 " when a context has been allocated\n");
350 /* no extra bytes are added to void attributes */
351 oldsize
= na
->data_size
;
353 /* make sure size is valid for a raw encrypted stream */
354 if ((oldsize
& 511) != 2) {
355 ntfs_log_error("Bad raw encrypted stream\n");
358 /* read padding length from last two bytes of attribute */
359 if (ntfs_attr_pread(na
, oldsize
- 2, 2, &appended_bytes
) != 2) {
360 ntfs_log_error("Error reading padding length\n");
363 padding_length
= le16_to_cpu(appended_bytes
);
364 if (padding_length
> 511 || padding_length
> na
->data_size
-2) {
366 ntfs_log_error("invalid padding length %d for data_size %lld\n",
367 padding_length
, (long long)oldsize
);
370 newsize
= oldsize
- padding_length
- 2;
372 * truncate attribute to possibly free clusters allocated
373 * for the last two bytes, but do not truncate to new size
374 * to avoid losing useful data
376 if (ntfs_attr_truncate(na
, oldsize
- 2)) {
377 ntfs_log_error("Error truncating attribute\n");
384 * Encrypted AT_DATA Attributes MUST be non-resident
385 * This has to be done after the attribute is resized, as
386 * resizing down to zero may cause the attribute to be made
389 if (!NAttrNonResident(na
)
390 && ntfs_attr_make_non_resident(na
, ctx
)) {
392 || ntfs_attr_force_non_resident(na
)) {
393 ntfs_log_error("Error making DATA attribute non-resident\n");
397 * must reinitialize context after forcing
398 * non-resident. We need a context for updating
399 * the state, and at this point, we are sure
400 * the context is not used elsewhere.
402 ntfs_attr_reinit_search_ctx(ctx
);
403 if (ntfs_attr_lookup(AT_DATA
, na
->name
, na
->name_len
,
404 CASE_SENSITIVE
, 0, NULL
, 0, ctx
)) {
405 ntfs_log_error("attr lookup for AT_DATA attribute failed in efs fixup\n");
412 ni
->data_size
= newsize
;
413 ni
->allocated_size
= na
->allocated_size
;
416 NInoFileNameSetDirty(ni
);
418 ctx
->attr
->data_size
= cpu_to_sle64(newsize
);
419 if (sle64_to_cpu(ctx
->attr
->initialized_size
) > newsize
)
420 ctx
->attr
->initialized_size
= ctx
->attr
->data_size
;
421 ctx
->attr
->flags
|= ATTR_IS_ENCRYPTED
;
423 ntfs_attr_put_search_ctx(ctx
);
427 if (close_ctx
&& ctx
)
428 ntfs_attr_put_search_ctx(ctx
);