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
43 #include <sys/xattr.h>
46 #ifdef HAVE_SYS_SYSMACROS_H
47 #include <sys/sysmacros.h>
61 #ifdef HAVE_SETXATTR /* extended attributes interface required */
63 static ntfschar logged_utility_stream_name
[] = {
64 const_cpu_to_le16('$'),
65 const_cpu_to_le16('E'),
66 const_cpu_to_le16('F'),
67 const_cpu_to_le16('S'),
73 * Get the ntfs EFS info into an extended attribute
76 int ntfs_get_efs_info(ntfs_inode
*ni
, char *value
, size_t size
)
78 EFS_ATTR_HEADER
*efs_info
;
82 if (ni
->flags
& FILE_ATTR_ENCRYPTED
) {
83 efs_info
= (EFS_ATTR_HEADER
*)ntfs_attr_readall(ni
,
84 AT_LOGGED_UTILITY_STREAM
,(ntfschar
*)NULL
, 0,
87 && (le32_to_cpu(efs_info
->length
) == attr_size
)) {
88 if (attr_size
<= (s64
)size
) {
90 memcpy(value
,efs_info
,attr_size
);
104 ntfs_log_error("Bad efs_info for inode %lld\n",
105 (long long)ni
->mft_no
);
107 ntfs_log_error("Could not get efsinfo"
109 (long long)ni
->mft_no
);
116 ntfs_log_trace("Inode %lld is not encrypted\n",
117 (long long)ni
->mft_no
);
120 return (attr_size
? (int)attr_size
: -errno
);
124 * Fix all encrypted AT_DATA attributes of an inode
126 * The fix may require making an attribute non resident, which
127 * requires more space in the MFT record, and may cause some
128 * attribute to be expelled and the full record to be reorganized.
129 * When this happens, the search for data attributes has to be
132 * Returns zero if successful.
133 * -1 if there is a problem.
136 static int fixup_loop(ntfs_inode
*ni
)
138 ntfs_attr_search_ctx
*ctx
;
149 ctx
= ntfs_attr_get_search_ctx(ni
, NULL
);
151 ntfs_log_error("Failed to get ctx for efs\n");
155 while (!restart
&& !res
156 && !ntfs_attr_lookup(AT_DATA
, NULL
, 0,
157 CASE_SENSITIVE
, 0, NULL
, 0, ctx
)) {
160 na
= ntfs_attr_open(ctx
->ntfs_ino
, AT_DATA
,
161 (ntfschar
*)((u8
*)a
+ le16_to_cpu(a
->name_offset
)),
164 ntfs_log_error("can't open DATA Attribute\n");
167 if (na
&& !(ctx
->attr
->flags
& ATTR_IS_ENCRYPTED
)) {
168 if (!NAttrNonResident(na
)
169 && ntfs_attr_make_non_resident(na
, ctx
)) {
171 * ntfs_attr_make_non_resident fails if there
172 * is not enough space in the MFT record.
173 * When this happens, force making non-resident
174 * so that some other attribute is expelled.
176 if (ntfs_attr_force_non_resident(na
)) {
179 /* make sure there is some progress */
182 ntfs_log_error("Multiple failure"
183 " making non resident\n");
186 ntfs_attr_put_search_ctx(ctx
);
187 ctx
= (ntfs_attr_search_ctx
*)NULL
;
194 && ntfs_efs_fixup_attribute(ctx
, na
)) {
195 ntfs_log_error("Error in efs fixup of AT_DATA Attribute\n");
202 } while (restart
&& !res
);
204 ntfs_attr_put_search_ctx(ctx
);
209 * Set the efs data from an extended attribute
210 * Warning : the new data is not checked
211 * Returns 0, or -1 if there is a problem
214 int ntfs_set_efs_info(ntfs_inode
*ni
, const char *value
, size_t size
,
221 const EFS_ATTR_HEADER
*info_header
;
224 if (ni
&& value
&& size
) {
225 if (ni
->flags
& (FILE_ATTR_ENCRYPTED
| FILE_ATTR_COMPRESSED
)) {
226 if (ni
->flags
& FILE_ATTR_ENCRYPTED
) {
227 ntfs_log_trace("Inode %lld already encrypted\n",
228 (long long)ni
->mft_no
);
232 * Possible problem : if encrypted file was
233 * restored in a compressed directory, it was
234 * restored as compressed.
235 * TODO : decompress first.
237 ntfs_log_error("Inode %lld cannot be encrypted and compressed\n",
238 (long long)ni
->mft_no
);
243 info_header
= (const EFS_ATTR_HEADER
*)value
;
244 /* make sure we get a likely efsinfo */
245 if (le32_to_cpu(info_header
->length
) != size
) {
249 if (!ntfs_attr_exist(ni
,AT_LOGGED_UTILITY_STREAM
,
250 (ntfschar
*)NULL
,0)) {
251 if (!(flags
& XATTR_REPLACE
)) {
253 * no logged_utility_stream attribute : add one,
254 * apparently, this does not feed the new value in
256 res
= ntfs_attr_add(ni
,AT_LOGGED_UTILITY_STREAM
,
257 logged_utility_stream_name
,4,
258 (u8
*)NULL
,(s64
)size
);
269 * open and update the existing efs data
271 na
= ntfs_attr_open(ni
, AT_LOGGED_UTILITY_STREAM
,
272 logged_utility_stream_name
, 4);
274 /* resize attribute */
275 res
= ntfs_attr_truncate(na
, (s64
)size
);
276 /* overwrite value if any */
278 written
= (int)ntfs_attr_pwrite(na
,
279 (s64
)0, (s64
)size
, value
);
280 if (written
!= (s64
)size
) {
281 ntfs_log_error("Failed to "
282 "update efs data\n");
292 /* Don't handle AT_DATA Attribute(s) if inode is a directory */
293 if (!(ni
->mrec
->flags
& MFT_RECORD_IS_DIRECTORY
)) {
294 /* iterate over AT_DATA attributes */
295 /* set encrypted flag, truncate attribute to match padding bytes */
300 ni
->flags
|= FILE_ATTR_ENCRYPTED
;
302 NInoFileNameSetDirty(ni
);
308 return (res
? -1 : 0);
312 * Fixup raw encrypted AT_DATA Attribute
313 * read padding length from last two bytes
314 * truncate attribute, make non-resident,
315 * set data size to match padding length
316 * set ATTR_IS_ENCRYPTED flag on attribute
318 * Return 0 if successful
319 * -1 if failed (errno tells why)
322 int ntfs_efs_fixup_attribute(ntfs_attr_search_ctx
*ctx
, ntfs_attr
*na
)
329 BOOL close_ctx
= FALSE
;
332 ntfs_log_error("no na specified for efs_fixup_attribute\n");
336 ctx
= ntfs_attr_get_search_ctx(na
->ni
, NULL
);
338 ntfs_log_error("Failed to get ctx for efs\n");
342 if (ntfs_attr_lookup(AT_DATA
, na
->name
, na
->name_len
,
343 CASE_SENSITIVE
, 0, NULL
, 0, ctx
)) {
344 ntfs_log_error("attr lookup for AT_DATA attribute failed in efs fixup\n");
348 if (!NAttrNonResident(na
)) {
349 ntfs_log_error("Cannot make non resident"
350 " when a context has been allocated\n");
355 /* no extra bytes are added to void attributes */
356 oldsize
= na
->data_size
;
358 /* make sure size is valid for a raw encrypted stream */
359 if ((oldsize
& 511) != 2) {
360 ntfs_log_error("Bad raw encrypted stream\n");
363 /* read padding length from last two bytes of attribute */
364 if (ntfs_attr_pread(na
, oldsize
- 2, 2, &appended_bytes
) != 2) {
365 ntfs_log_error("Error reading padding length\n");
368 padding_length
= le16_to_cpu(appended_bytes
);
369 if (padding_length
> 511 || padding_length
> na
->data_size
-2) {
371 ntfs_log_error("invalid padding length %d for data_size %lld\n",
372 padding_length
, (long long)oldsize
);
375 newsize
= oldsize
- padding_length
- 2;
377 * truncate attribute to possibly free clusters allocated
378 * for the last two bytes, but do not truncate to new size
379 * to avoid losing useful data
381 if (ntfs_attr_truncate(na
, oldsize
- 2)) {
382 ntfs_log_error("Error truncating attribute\n");
389 * Encrypted AT_DATA Attributes MUST be non-resident
390 * This has to be done after the attribute is resized, as
391 * resizing down to zero may cause the attribute to be made
394 if (!NAttrNonResident(na
)
395 && ntfs_attr_make_non_resident(na
, ctx
)) {
397 || ntfs_attr_force_non_resident(na
)) {
398 ntfs_log_error("Error making DATA attribute non-resident\n");
402 * must reinitialize context after forcing
403 * non-resident. We need a context for updating
404 * the state, and at this point, we are sure
405 * the context is not used elsewhere.
407 ntfs_attr_reinit_search_ctx(ctx
);
408 if (ntfs_attr_lookup(AT_DATA
, na
->name
, na
->name_len
,
409 CASE_SENSITIVE
, 0, NULL
, 0, ctx
)) {
410 ntfs_log_error("attr lookup for AT_DATA attribute failed in efs fixup\n");
417 ni
->data_size
= newsize
;
418 ni
->allocated_size
= na
->allocated_size
;
421 NInoFileNameSetDirty(ni
);
423 ctx
->attr
->data_size
= cpu_to_le64(newsize
);
424 if (le64_to_cpu(ctx
->attr
->initialized_size
) > newsize
)
425 ctx
->attr
->initialized_size
= ctx
->attr
->data_size
;
426 ctx
->attr
->flags
|= ATTR_IS_ENCRYPTED
;
428 ntfs_attr_put_search_ctx(ctx
);
432 if (close_ctx
&& ctx
)
433 ntfs_attr_put_search_ctx(ctx
);
437 #endif /* HAVE_SETXATTR */