blob: 22bec88f96c2abc4903debc741ac46950d743c63
1 | /** |
2 | * ntfs-3g - Third Generation NTFS Driver |
3 | * |
4 | * Copyright (c) 2005-2007 Yura Pakhuchiy |
5 | * Copyright (c) 2005 Yuval Fledel |
6 | * Copyright (c) 2006-2009 Szabolcs Szakacsits |
7 | * Copyright (c) 2007-2013 Jean-Pierre Andre |
8 | * Copyright (c) 2009 Erik Larsson |
9 | * |
10 | * This file is originated from the Linux-NTFS project. |
11 | * |
12 | * This program is free software; you can redistribute it and/or modify |
13 | * it under the terms of the GNU General Public License as published by |
14 | * the Free Software Foundation; either version 2 of the License, or |
15 | * (at your option) any later version. |
16 | * |
17 | * This program is distributed in the hope that it will be useful, |
18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
20 | * GNU General Public License for more details. |
21 | * |
22 | * You should have received a copy of the GNU General Public License |
23 | * along with this program (in the main directory of the NTFS-3G |
24 | * distribution in the file COPYING); if not, write to the Free Software |
25 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
26 | */ |
27 | |
28 | #include "config.h" |
29 | |
30 | #include <fuse.h> |
31 | |
32 | #if !defined(FUSE_VERSION) || (FUSE_VERSION < 26) |
33 | #error "***********************************************************" |
34 | #error "* *" |
35 | #error "* Compilation requires at least FUSE version 2.6.0! *" |
36 | #error "* *" |
37 | #error "***********************************************************" |
38 | #endif |
39 | |
40 | #include <sys/sysmacros.h> |
41 | #ifdef HAVE_STDIO_H |
42 | #include <stdio.h> |
43 | #endif |
44 | #ifdef HAVE_STRING_H |
45 | #include <string.h> |
46 | #endif |
47 | #ifdef HAVE_ERRNO_H |
48 | #include <errno.h> |
49 | #endif |
50 | #ifdef HAVE_FCNTL_H |
51 | #include <fcntl.h> |
52 | #endif |
53 | #ifdef HAVE_UNISTD_H |
54 | #include <unistd.h> |
55 | #endif |
56 | #ifdef HAVE_STDLIB_H |
57 | #include <stdlib.h> |
58 | #endif |
59 | #ifdef HAVE_LOCALE_H |
60 | #include <locale.h> |
61 | #endif |
62 | #include <signal.h> |
63 | #ifdef HAVE_LIMITS_H |
64 | #include <limits.h> |
65 | #endif |
66 | #include <syslog.h> |
67 | #include <sys/wait.h> |
68 | |
69 | #ifdef HAVE_SETXATTR |
70 | #include <sys/xattr.h> |
71 | #endif |
72 | |
73 | #ifdef HAVE_SYS_TYPES_H |
74 | #include <sys/types.h> |
75 | #endif |
76 | #ifdef HAVE_SYS_MKDEV_H |
77 | #include <sys/mkdev.h> |
78 | #endif |
79 | |
80 | #if defined(__APPLE__) || defined(__DARWIN__) |
81 | #include <sys/dirent.h> |
82 | #endif /* defined(__APPLE__) || defined(__DARWIN__) */ |
83 | |
84 | #include "compat.h" |
85 | #include "attrib.h" |
86 | #include "inode.h" |
87 | #include "volume.h" |
88 | #include "dir.h" |
89 | #include "unistr.h" |
90 | #include "layout.h" |
91 | #include "index.h" |
92 | #include "ntfstime.h" |
93 | #include "security.h" |
94 | #include "reparse.h" |
95 | #include "object_id.h" |
96 | #include "efs.h" |
97 | #include "logging.h" |
98 | #include "xattrs.h" |
99 | #include "misc.h" |
100 | |
101 | #include "ntfs-3g_common.h" |
102 | |
103 | /* |
104 | * The following permission checking modes are governed by |
105 | * the HPERMSCONFIG value in param.h |
106 | */ |
107 | |
108 | /* ACLS may be checked by kernel (requires a fuse patch) or here */ |
109 | #define KERNELACLS ((HPERMSCONFIG > 6) & (HPERMSCONFIG < 10)) |
110 | /* basic permissions may be checked by kernel or here */ |
111 | #define KERNELPERMS (((HPERMSCONFIG - 1) % 6) < 3) |
112 | /* may want to use fuse/kernel cacheing */ |
113 | #define CACHEING (!(HPERMSCONFIG % 3)) |
114 | |
115 | #if KERNELACLS & !KERNELPERMS |
116 | #error Incompatible options KERNELACLS and KERNELPERMS |
117 | #endif |
118 | |
119 | /* sometimes the kernel cannot check access */ |
120 | #define ntfs_real_allowed_access(scx, ni, type) ntfs_allowed_access(scx, ni, type) |
121 | #if POSIXACLS & KERNELPERMS & !KERNELACLS |
122 | /* short-circuit if PERMS checked by kernel and ACLs by fs */ |
123 | #define ntfs_allowed_access(scx, ni, type) \ |
124 | ((scx)->vol->secure_flags & (1 << SECURITY_DEFAULT) \ |
125 | ? 1 : ntfs_allowed_access(scx, ni, type)) |
126 | #endif |
127 | |
128 | #define set_archive(ni) (ni)->flags |= FILE_ATTR_ARCHIVE |
129 | |
130 | #ifdef ANDROID |
131 | #define LOG_TAG "Ntfs-3g" |
132 | #endif |
133 | |
134 | typedef enum { |
135 | FSTYPE_NONE, |
136 | FSTYPE_UNKNOWN, |
137 | FSTYPE_FUSE, |
138 | FSTYPE_FUSEBLK |
139 | } fuse_fstype; |
140 | |
141 | typedef struct { |
142 | fuse_fill_dir_t filler; |
143 | void *buf; |
144 | } ntfs_fuse_fill_context_t; |
145 | |
146 | enum { |
147 | CLOSE_COMPRESSED = 1, |
148 | CLOSE_ENCRYPTED = 2, |
149 | CLOSE_DMTIME = 4 |
150 | }; |
151 | |
152 | static struct ntfs_options opts; |
153 | |
154 | const char *EXEC_NAME = "ntfs-3g"; |
155 | |
156 | static ntfs_fuse_context_t *ctx; |
157 | static u32 ntfs_sequence; |
158 | |
159 | static const char *usage_msg = |
160 | "\n" |
161 | "%s %s %s %d - Third Generation NTFS Driver\n" |
162 | "\t\tConfiguration type %d, " |
163 | #ifdef HAVE_SETXATTR |
164 | "XATTRS are on, " |
165 | #else |
166 | "XATTRS are off, " |
167 | #endif |
168 | #if POSIXACLS |
169 | "POSIX ACLS are on\n" |
170 | #else |
171 | "POSIX ACLS are off\n" |
172 | #endif |
173 | "\n" |
174 | "Copyright (C) 2005-2007 Yura Pakhuchiy\n" |
175 | "Copyright (C) 2006-2009 Szabolcs Szakacsits\n" |
176 | "Copyright (C) 2007-2012 Jean-Pierre Andre\n" |
177 | "Copyright (C) 2009 Erik Larsson\n" |
178 | "\n" |
179 | "Usage: %s [-o option[,...]] <device|image_file> <mount_point>\n" |
180 | "\n" |
181 | "Options: ro (read-only mount), windows_names, uid=, gid=,\n" |
182 | " umask=, fmask=, dmask=, streams_interface=.\n" |
183 | " Please see the details in the manual (type: man ntfs-3g).\n" |
184 | "\n" |
185 | "Example: ntfs-3g /dev/sda1 /mnt/windows\n" |
186 | "\n" |
187 | "%s"; |
188 | |
189 | static const char ntfs_bad_reparse[] = "unsupported reparse point"; |
190 | |
191 | #ifdef FUSE_INTERNAL |
192 | int drop_privs(void); |
193 | int restore_privs(void); |
194 | #else |
195 | /* |
196 | * setuid and setgid root ntfs-3g denies to start with external FUSE, |
197 | * therefore the below functions are no-op in such case. |
198 | */ |
199 | static int drop_privs(void) { return 0; } |
200 | #if defined(linux) || defined(__uClinux__) |
201 | static int restore_privs(void) { return 0; } |
202 | #endif |
203 | |
204 | static const char *setuid_msg = |
205 | "Mount is denied because setuid and setgid root ntfs-3g is insecure with the\n" |
206 | "external FUSE library. Either remove the setuid/setgid bit from the binary\n" |
207 | "or rebuild NTFS-3G with integrated FUSE support and make it setuid root.\n" |
208 | "Please see more information at\n" |
209 | "http://tuxera.com/community/ntfs-3g-faq/#unprivileged\n"; |
210 | |
211 | static const char *unpriv_fuseblk_msg = |
212 | "Unprivileged user can not mount NTFS block devices using the external FUSE\n" |
213 | "library. Either mount the volume as root, or rebuild NTFS-3G with integrated\n" |
214 | "FUSE support and make it setuid root. Please see more information at\n" |
215 | "http://tuxera.com/community/ntfs-3g-faq/#unprivileged\n"; |
216 | #endif |
217 | |
218 | |
219 | /** |
220 | * ntfs_fuse_is_named_data_stream - check path to be to named data stream |
221 | * @path: path to check |
222 | * |
223 | * Returns 1 if path is to named data stream or 0 otherwise. |
224 | */ |
225 | static int ntfs_fuse_is_named_data_stream(const char *path) |
226 | { |
227 | if (strchr(path, ':') && ctx->streams == NF_STREAMS_INTERFACE_WINDOWS) |
228 | return 1; |
229 | return 0; |
230 | } |
231 | |
232 | static void ntfs_fuse_update_times(ntfs_inode *ni, ntfs_time_update_flags mask) |
233 | { |
234 | if (ctx->atime == ATIME_DISABLED) |
235 | mask &= ~NTFS_UPDATE_ATIME; |
236 | else if (ctx->atime == ATIME_RELATIVE && mask == NTFS_UPDATE_ATIME && |
237 | (le64_to_cpu(ni->last_access_time) |
238 | >= le64_to_cpu(ni->last_data_change_time)) && |
239 | (le64_to_cpu(ni->last_access_time) |
240 | >= le64_to_cpu(ni->last_mft_change_time))) |
241 | return; |
242 | ntfs_inode_update_times(ni, mask); |
243 | } |
244 | |
245 | static s64 ntfs_get_nr_free_mft_records(ntfs_volume *vol) |
246 | { |
247 | ntfs_attr *na = vol->mftbmp_na; |
248 | s64 nr_free = ntfs_attr_get_free_bits(na); |
249 | |
250 | if (nr_free >= 0) |
251 | nr_free += (na->allocated_size - na->data_size) << 3; |
252 | return nr_free; |
253 | } |
254 | |
255 | /* |
256 | * Fill a security context as needed by security functions |
257 | * returns TRUE if there is a user mapping, |
258 | * FALSE if there is none |
259 | * This is not an error and the context is filled anyway, |
260 | * it is used for implicit Windows-like inheritance |
261 | */ |
262 | |
263 | static BOOL ntfs_fuse_fill_security_context(struct SECURITY_CONTEXT *scx) |
264 | { |
265 | struct fuse_context *fusecontext; |
266 | |
267 | scx->vol = ctx->vol; |
268 | scx->mapping[MAPUSERS] = ctx->security.mapping[MAPUSERS]; |
269 | scx->mapping[MAPGROUPS] = ctx->security.mapping[MAPGROUPS]; |
270 | scx->pseccache = &ctx->seccache; |
271 | fusecontext = fuse_get_context(); |
272 | scx->uid = fusecontext->uid; |
273 | scx->gid = fusecontext->gid; |
274 | scx->tid = fusecontext->pid; |
275 | #ifdef FUSE_CAP_DONT_MASK |
276 | /* the umask can be processed by the file system */ |
277 | scx->umask = fusecontext->umask; |
278 | #else |
279 | /* the umask if forced by fuse on creation */ |
280 | scx->umask = 0; |
281 | #endif |
282 | |
283 | return (ctx->security.mapping[MAPUSERS] != (struct MAPPING*)NULL); |
284 | } |
285 | |
286 | #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) |
287 | |
288 | /* |
289 | * Check access to parent directory |
290 | * |
291 | * directory and file inodes are only opened when not fed in, |
292 | * they *HAVE TO* be fed in when already open, however |
293 | * file inode is only useful when S_ISVTX is requested |
294 | * |
295 | * returns 1 if allowed, |
296 | * 0 if not allowed or some error occurred (errno tells why) |
297 | */ |
298 | |
299 | static int ntfs_allowed_dir_access(struct SECURITY_CONTEXT *scx, |
300 | const char *path, ntfs_inode *dir_ni, |
301 | ntfs_inode *ni, mode_t accesstype) |
302 | { |
303 | int allowed; |
304 | ntfs_inode *ni2; |
305 | ntfs_inode *dir_ni2; |
306 | char *dirpath; |
307 | char *name; |
308 | struct stat stbuf; |
309 | |
310 | #if POSIXACLS & KERNELPERMS & !KERNELACLS |
311 | /* short-circuit if PERMS checked by kernel and ACLs by fs */ |
312 | if (scx->vol->secure_flags & (1 << SECURITY_DEFAULT)) |
313 | allowed = 1; |
314 | else |
315 | #endif |
316 | if (dir_ni) |
317 | allowed = ntfs_real_allowed_access(scx, dir_ni, |
318 | accesstype); |
319 | else { |
320 | allowed = 0; |
321 | dirpath = strdup(path); |
322 | if (dirpath) { |
323 | /* the root of file system is seen as a parent of itself */ |
324 | /* is that correct ? */ |
325 | name = strrchr(dirpath, '/'); |
326 | *name = 0; |
327 | dir_ni2 = ntfs_pathname_to_inode(scx->vol, |
328 | NULL, dirpath); |
329 | if (dir_ni2) { |
330 | allowed = ntfs_real_allowed_access(scx, |
331 | dir_ni2, accesstype); |
332 | if (ntfs_inode_close(dir_ni2)) |
333 | allowed = 0; |
334 | } |
335 | free(dirpath); |
336 | } |
337 | } |
338 | /* |
339 | * for a not-owned sticky directory, have to |
340 | * check whether file itself is owned |
341 | */ |
342 | if ((accesstype == (S_IWRITE + S_IEXEC + S_ISVTX)) |
343 | && (allowed == 2)) { |
344 | if (ni) |
345 | ni2 = ni; |
346 | else |
347 | ni2 = ntfs_pathname_to_inode(scx->vol, NULL, |
348 | path); |
349 | allowed = 0; |
350 | if (ni2) { |
351 | allowed = (ntfs_get_owner_mode(scx,ni2,&stbuf) |
352 | >= 0) |
353 | && (stbuf.st_uid == scx->uid); |
354 | if (!ni) |
355 | ntfs_inode_close(ni2); |
356 | } |
357 | } |
358 | return (allowed); |
359 | } |
360 | |
361 | #endif |
362 | |
363 | #ifdef HAVE_SETXATTR /* extended attributes interface required */ |
364 | |
365 | /* |
366 | * Check access to parent directory |
367 | * |
368 | * for non-standard cases where access control cannot be checked by kernel |
369 | * |
370 | * no known situations where S_ISVTX is requested |
371 | * |
372 | * returns 1 if allowed, |
373 | * 0 if not allowed or some error occurred (errno tells why) |
374 | */ |
375 | |
376 | static int ntfs_allowed_real_dir_access(struct SECURITY_CONTEXT *scx, |
377 | const char *path, ntfs_inode *dir_ni, |
378 | mode_t accesstype) |
379 | { |
380 | int allowed; |
381 | ntfs_inode *dir_ni2; |
382 | char *dirpath; |
383 | char *name; |
384 | |
385 | if (dir_ni) |
386 | allowed = ntfs_real_allowed_access(scx, dir_ni, accesstype); |
387 | else { |
388 | allowed = 0; |
389 | dirpath = strdup(path); |
390 | if (dirpath) { |
391 | /* the root of file system is seen as a parent of itself */ |
392 | /* is that correct ? */ |
393 | name = strrchr(dirpath, '/'); |
394 | *name = 0; |
395 | dir_ni2 = ntfs_pathname_to_inode(scx->vol, NULL, |
396 | dirpath); |
397 | if (dir_ni2) { |
398 | allowed = ntfs_real_allowed_access(scx, |
399 | dir_ni2, accesstype); |
400 | if (ntfs_inode_close(dir_ni2)) |
401 | allowed = 0; |
402 | } |
403 | free(dirpath); |
404 | } |
405 | } |
406 | return (allowed); |
407 | } |
408 | |
409 | static ntfs_inode *get_parent_dir(const char *path) |
410 | { |
411 | ntfs_inode *dir_ni; |
412 | char *dirpath; |
413 | char *p; |
414 | |
415 | dirpath = strdup(path); |
416 | dir_ni = (ntfs_inode*)NULL; |
417 | if (dirpath) { |
418 | p = strrchr(dirpath,'/'); |
419 | if (p) { /* always present, be safe */ |
420 | *p = 0; |
421 | dir_ni = ntfs_pathname_to_inode(ctx->vol, |
422 | NULL, dirpath); |
423 | } |
424 | free(dirpath); |
425 | } else |
426 | errno = ENOMEM; |
427 | return (dir_ni); |
428 | } |
429 | |
430 | |
431 | #endif /* HAVE_SETXATTR */ |
432 | |
433 | /** |
434 | * ntfs_fuse_statfs - return information about mounted NTFS volume |
435 | * @path: ignored (but fuse requires it) |
436 | * @sfs: statfs structure in which to return the information |
437 | * |
438 | * Return information about the mounted NTFS volume @sb in the statfs structure |
439 | * pointed to by @sfs (this is initialized with zeros before ntfs_statfs is |
440 | * called). We interpret the values to be correct of the moment in time at |
441 | * which we are called. Most values are variable otherwise and this isn't just |
442 | * the free values but the totals as well. For example we can increase the |
443 | * total number of file nodes if we run out and we can keep doing this until |
444 | * there is no more space on the volume left at all. |
445 | * |
446 | * This code based on ntfs_statfs from ntfs kernel driver. |
447 | * |
448 | * Returns 0 on success or -errno on error. |
449 | */ |
450 | #if HAVE_SYS_STATVFS_H |
451 | static int ntfs_fuse_statfs(const char *path __attribute__((unused)), |
452 | struct statvfs *sfs) |
453 | { |
454 | s64 size; |
455 | int delta_bits; |
456 | ntfs_volume *vol; |
457 | |
458 | vol = ctx->vol; |
459 | if (!vol) |
460 | return -ENODEV; |
461 | |
462 | /* |
463 | * File system block size. Used to calculate used/free space by df. |
464 | * Incorrectly documented as "optimal transfer block size". |
465 | */ |
466 | sfs->f_bsize = vol->cluster_size; |
467 | |
468 | /* Fundamental file system block size, used as the unit. */ |
469 | sfs->f_frsize = vol->cluster_size; |
470 | |
471 | /* |
472 | * Total number of blocks on file system in units of f_frsize. |
473 | * Since inodes are also stored in blocks ($MFT is a file) hence |
474 | * this is the number of clusters on the volume. |
475 | */ |
476 | sfs->f_blocks = vol->nr_clusters; |
477 | |
478 | /* Free blocks available for all and for non-privileged processes. */ |
479 | size = vol->free_clusters; |
480 | if (size < 0) |
481 | size = 0; |
482 | sfs->f_bavail = sfs->f_bfree = size; |
483 | |
484 | /* Free inodes on the free space */ |
485 | delta_bits = vol->cluster_size_bits - vol->mft_record_size_bits; |
486 | if (delta_bits >= 0) |
487 | size <<= delta_bits; |
488 | else |
489 | size >>= -delta_bits; |
490 | |
491 | /* Number of inodes at this point in time. */ |
492 | sfs->f_files = (vol->mftbmp_na->allocated_size << 3) + size; |
493 | |
494 | /* Free inodes available for all and for non-privileged processes. */ |
495 | size += vol->free_mft_records; |
496 | if (size < 0) |
497 | size = 0; |
498 | sfs->f_ffree = sfs->f_favail = size; |
499 | |
500 | /* Maximum length of filenames. */ |
501 | sfs->f_namemax = NTFS_MAX_NAME_LEN; |
502 | return 0; |
503 | } |
504 | #endif |
505 | |
506 | /** |
507 | * ntfs_fuse_parse_path - split path to path and stream name. |
508 | * @org_path: path to split |
509 | * @path: pointer to buffer in which parsed path saved |
510 | * @stream_name: pointer to buffer where stream name in unicode saved |
511 | * |
512 | * This function allocates buffers for @*path and @*stream, user must free them |
513 | * after use. |
514 | * |
515 | * Return values: |
516 | * <0 Error occurred, return -errno; |
517 | * 0 No stream name, @*stream is not allocated and set to AT_UNNAMED. |
518 | * >0 Stream name length in unicode characters. |
519 | */ |
520 | static int ntfs_fuse_parse_path(const char *org_path, char **path, |
521 | ntfschar **stream_name) |
522 | { |
523 | char *stream_name_mbs; |
524 | int res; |
525 | |
526 | stream_name_mbs = strdup(org_path); |
527 | if (!stream_name_mbs) |
528 | return -errno; |
529 | if (ctx->streams == NF_STREAMS_INTERFACE_WINDOWS) { |
530 | *path = strsep(&stream_name_mbs, ":"); |
531 | if (stream_name_mbs) { |
532 | *stream_name = NULL; |
533 | res = ntfs_mbstoucs(stream_name_mbs, stream_name); |
534 | if (res < 0) |
535 | return -errno; |
536 | return res; |
537 | } |
538 | } else |
539 | *path = stream_name_mbs; |
540 | *stream_name = AT_UNNAMED; |
541 | return 0; |
542 | } |
543 | |
544 | static void set_fuse_error(int *err) |
545 | { |
546 | if (!*err) |
547 | *err = -errno; |
548 | } |
549 | |
550 | #if defined(__APPLE__) || defined(__DARWIN__) |
551 | static int ntfs_macfuse_getxtimes(const char *org_path, |
552 | struct timespec *bkuptime, struct timespec *crtime) |
553 | { |
554 | int res = 0; |
555 | ntfs_inode *ni; |
556 | char *path = NULL; |
557 | ntfschar *stream_name; |
558 | int stream_name_len; |
559 | |
560 | stream_name_len = ntfs_fuse_parse_path(org_path, &path, &stream_name); |
561 | if (stream_name_len < 0) |
562 | return stream_name_len; |
563 | memset(bkuptime, 0, sizeof(struct timespec)); |
564 | memset(crtime, 0, sizeof(struct timespec)); |
565 | ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); |
566 | if (!ni) { |
567 | res = -errno; |
568 | goto exit; |
569 | } |
570 | |
571 | /* We have no backup timestamp in NTFS. */ |
572 | crtime->tv_sec = ni->creation_time; |
573 | exit: |
574 | if (ntfs_inode_close(ni)) |
575 | set_fuse_error(&res); |
576 | free(path); |
577 | if (stream_name_len) |
578 | free(stream_name); |
579 | return res; |
580 | } |
581 | |
582 | int ntfs_macfuse_setcrtime(const char *path, const struct timespec *tv) |
583 | { |
584 | ntfs_inode *ni; |
585 | int res = 0; |
586 | |
587 | if (ntfs_fuse_is_named_data_stream(path)) |
588 | return -EINVAL; /* n/a for named data streams. */ |
589 | ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); |
590 | if (!ni) |
591 | return -errno; |
592 | |
593 | if (tv) { |
594 | ni->creation_time = tv->tv_sec; |
595 | ntfs_fuse_update_times(ni, NTFS_UPDATE_CTIME); |
596 | } |
597 | |
598 | if (ntfs_inode_close(ni)) |
599 | set_fuse_error(&res); |
600 | return res; |
601 | } |
602 | |
603 | int ntfs_macfuse_setbkuptime(const char *path, const struct timespec *tv) |
604 | { |
605 | ntfs_inode *ni; |
606 | int res = 0; |
607 | |
608 | if (ntfs_fuse_is_named_data_stream(path)) |
609 | return -EINVAL; /* n/a for named data streams. */ |
610 | ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); |
611 | if (!ni) |
612 | return -errno; |
613 | |
614 | /* |
615 | * Only pretending to set backup time successfully to please the APIs of |
616 | * Mac OS X. In reality, NTFS has no backup time. |
617 | */ |
618 | |
619 | if (ntfs_inode_close(ni)) |
620 | set_fuse_error(&res); |
621 | return res; |
622 | } |
623 | |
624 | int ntfs_macfuse_setchgtime(const char *path, const struct timespec *tv) |
625 | { |
626 | ntfs_inode *ni; |
627 | int res = 0; |
628 | |
629 | if (ntfs_fuse_is_named_data_stream(path)) |
630 | return -EINVAL; /* n/a for named data streams. */ |
631 | ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); |
632 | if (!ni) |
633 | return -errno; |
634 | |
635 | if (tv) { |
636 | ni->last_mft_change_time = tv->tv_sec; |
637 | ntfs_fuse_update_times(ni, 0); |
638 | } |
639 | |
640 | if (ntfs_inode_close(ni)) |
641 | set_fuse_error(&res); |
642 | return res; |
643 | } |
644 | #endif /* defined(__APPLE__) || defined(__DARWIN__) */ |
645 | |
646 | #if defined(FUSE_CAP_DONT_MASK) || defined(FUSE_CAP_BIG_WRITES) \ |
647 | || (defined(__APPLE__) || defined(__DARWIN__)) |
648 | static void *ntfs_init(struct fuse_conn_info *conn) |
649 | { |
650 | #if defined(__APPLE__) || defined(__DARWIN__) |
651 | FUSE_ENABLE_XTIMES(conn); |
652 | #endif |
653 | #ifdef FUSE_CAP_DONT_MASK |
654 | /* request umask not to be enforced by fuse */ |
655 | conn->want |= FUSE_CAP_DONT_MASK; |
656 | #endif /* defined FUSE_CAP_DONT_MASK */ |
657 | #ifdef FUSE_CAP_BIG_WRITES |
658 | if (ctx->big_writes |
659 | && ((ctx->vol->nr_clusters << ctx->vol->cluster_size_bits) |
660 | >= SAFE_CAPACITY_FOR_BIG_WRITES)) |
661 | conn->want |= FUSE_CAP_BIG_WRITES; |
662 | #endif |
663 | return NULL; |
664 | } |
665 | #endif /* defined(FUSE_CAP_DONT_MASK) || (defined(__APPLE__) || defined(__DARWIN__)) */ |
666 | |
667 | static int ntfs_fuse_getattr(const char *org_path, struct stat *stbuf) |
668 | { |
669 | int res = 0; |
670 | ntfs_inode *ni; |
671 | ntfs_attr *na; |
672 | char *path = NULL; |
673 | ntfschar *stream_name; |
674 | int stream_name_len; |
675 | BOOL withusermapping; |
676 | struct SECURITY_CONTEXT security; |
677 | |
678 | stream_name_len = ntfs_fuse_parse_path(org_path, &path, &stream_name); |
679 | if (stream_name_len < 0) |
680 | return stream_name_len; |
681 | memset(stbuf, 0, sizeof(struct stat)); |
682 | ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); |
683 | if (!ni) { |
684 | res = -errno; |
685 | goto exit; |
686 | } |
687 | withusermapping = ntfs_fuse_fill_security_context(&security); |
688 | #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) |
689 | /* |
690 | * make sure the parent directory is searchable |
691 | */ |
692 | if (withusermapping |
693 | && !ntfs_allowed_dir_access(&security,path, |
694 | (!strcmp(org_path,"/") ? ni : (ntfs_inode*)NULL), |
695 | ni, S_IEXEC)) { |
696 | res = -EACCES; |
697 | goto exit; |
698 | } |
699 | #endif |
700 | if (((ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) |
701 | || (ni->flags & FILE_ATTR_REPARSE_POINT)) |
702 | && !stream_name_len) { |
703 | if (ni->flags & FILE_ATTR_REPARSE_POINT) { |
704 | char *target; |
705 | int attr_size; |
706 | |
707 | errno = 0; |
708 | target = ntfs_make_symlink(ni, ctx->abs_mnt_point, &attr_size); |
709 | /* |
710 | * If the reparse point is not a valid |
711 | * directory junction, and there is no error |
712 | * we still display as a symlink |
713 | */ |
714 | if (target || (errno == EOPNOTSUPP)) { |
715 | /* returning attribute size */ |
716 | if (target) |
717 | stbuf->st_size = attr_size; |
718 | else |
719 | stbuf->st_size = sizeof(ntfs_bad_reparse); |
720 | stbuf->st_blocks = (ni->allocated_size + 511) >> 9; |
721 | stbuf->st_nlink = le16_to_cpu(ni->mrec->link_count); |
722 | stbuf->st_mode = S_IFLNK; |
723 | free(target); |
724 | } else { |
725 | res = -errno; |
726 | goto exit; |
727 | } |
728 | } else { |
729 | /* Directory. */ |
730 | stbuf->st_mode = S_IFDIR | (0777 & ~ctx->dmask); |
731 | /* get index size, if not known */ |
732 | if (!test_nino_flag(ni, KnownSize)) { |
733 | na = ntfs_attr_open(ni, AT_INDEX_ALLOCATION, NTFS_INDEX_I30, 4); |
734 | if (na) { |
735 | ni->data_size = na->data_size; |
736 | ni->allocated_size = na->allocated_size; |
737 | set_nino_flag(ni, KnownSize); |
738 | ntfs_attr_close(na); |
739 | } |
740 | } |
741 | stbuf->st_size = ni->data_size; |
742 | stbuf->st_blocks = ni->allocated_size >> 9; |
743 | stbuf->st_nlink = 1; /* Make find(1) work */ |
744 | } |
745 | } else { |
746 | /* Regular or Interix (INTX) file. */ |
747 | stbuf->st_mode = S_IFREG; |
748 | stbuf->st_size = ni->data_size; |
749 | #ifdef HAVE_SETXATTR /* extended attributes interface required */ |
750 | /* |
751 | * return data size rounded to next 512 byte boundary for |
752 | * encrypted files to include padding required for decryption |
753 | * also include 2 bytes for padding info |
754 | */ |
755 | if (ctx->efs_raw |
756 | && (ni->flags & FILE_ATTR_ENCRYPTED) |
757 | && ni->data_size) |
758 | stbuf->st_size = ((ni->data_size + 511) & ~511) + 2; |
759 | #endif /* HAVE_SETXATTR */ |
760 | /* |
761 | * Temporary fix to make ActiveSync work via Samba 3.0. |
762 | * See more on the ntfs-3g-devel list. |
763 | */ |
764 | stbuf->st_blocks = (ni->allocated_size + 511) >> 9; |
765 | stbuf->st_nlink = le16_to_cpu(ni->mrec->link_count); |
766 | if (ni->flags & FILE_ATTR_SYSTEM || stream_name_len) { |
767 | na = ntfs_attr_open(ni, AT_DATA, stream_name, |
768 | stream_name_len); |
769 | if (!na) { |
770 | if (stream_name_len) { |
771 | res = -ENOENT; |
772 | goto exit; |
773 | } else |
774 | goto nodata; |
775 | } |
776 | if (stream_name_len) { |
777 | stbuf->st_size = na->data_size; |
778 | stbuf->st_blocks = na->allocated_size >> 9; |
779 | } |
780 | /* Check whether it's Interix FIFO or socket. */ |
781 | if (!(ni->flags & FILE_ATTR_HIDDEN) && |
782 | !stream_name_len) { |
783 | /* FIFO. */ |
784 | if (na->data_size == 0) |
785 | stbuf->st_mode = S_IFIFO; |
786 | /* Socket link. */ |
787 | if (na->data_size == 1) |
788 | stbuf->st_mode = S_IFSOCK; |
789 | } |
790 | #ifdef HAVE_SETXATTR /* extended attributes interface required */ |
791 | /* encrypted named stream */ |
792 | /* round size up to next 512 byte boundary */ |
793 | if (ctx->efs_raw && stream_name_len && |
794 | (na->data_flags & ATTR_IS_ENCRYPTED) && |
795 | NAttrNonResident(na)) |
796 | stbuf->st_size = ((na->data_size+511) & ~511)+2; |
797 | #endif /* HAVE_SETXATTR */ |
798 | /* |
799 | * Check whether it's Interix symbolic link, block or |
800 | * character device. |
801 | */ |
802 | if ((u64)na->data_size <= sizeof(INTX_FILE_TYPES) |
803 | + sizeof(ntfschar) * PATH_MAX |
804 | && (u64)na->data_size > |
805 | sizeof(INTX_FILE_TYPES) |
806 | && !stream_name_len) { |
807 | |
808 | INTX_FILE *intx_file; |
809 | |
810 | intx_file = ntfs_malloc(na->data_size); |
811 | if (!intx_file) { |
812 | res = -errno; |
813 | ntfs_attr_close(na); |
814 | goto exit; |
815 | } |
816 | if (ntfs_attr_pread(na, 0, na->data_size, |
817 | intx_file) != na->data_size) { |
818 | res = -errno; |
819 | free(intx_file); |
820 | ntfs_attr_close(na); |
821 | goto exit; |
822 | } |
823 | if (intx_file->magic == INTX_BLOCK_DEVICE && |
824 | na->data_size == offsetof( |
825 | INTX_FILE, device_end)) { |
826 | stbuf->st_mode = S_IFBLK; |
827 | stbuf->st_rdev = makedev(le64_to_cpu( |
828 | intx_file->major), |
829 | le64_to_cpu( |
830 | intx_file->minor)); |
831 | } |
832 | if (intx_file->magic == INTX_CHARACTER_DEVICE && |
833 | na->data_size == offsetof( |
834 | INTX_FILE, device_end)) { |
835 | stbuf->st_mode = S_IFCHR; |
836 | stbuf->st_rdev = makedev(le64_to_cpu( |
837 | intx_file->major), |
838 | le64_to_cpu( |
839 | intx_file->minor)); |
840 | } |
841 | if (intx_file->magic == INTX_SYMBOLIC_LINK) |
842 | stbuf->st_mode = S_IFLNK; |
843 | free(intx_file); |
844 | } |
845 | ntfs_attr_close(na); |
846 | } |
847 | stbuf->st_mode |= (0777 & ~ctx->fmask); |
848 | } |
849 | if (withusermapping) { |
850 | if (ntfs_get_owner_mode(&security,ni,stbuf) < 0) |
851 | set_fuse_error(&res); |
852 | } else { |
853 | stbuf->st_uid = ctx->uid; |
854 | stbuf->st_gid = ctx->gid; |
855 | } |
856 | if (S_ISLNK(stbuf->st_mode)) |
857 | stbuf->st_mode |= 0777; |
858 | nodata : |
859 | stbuf->st_ino = ni->mft_no; |
860 | #ifdef HAVE_STRUCT_STAT_ST_ATIMESPEC |
861 | stbuf->st_atimespec = ntfs2timespec(ni->last_access_time); |
862 | stbuf->st_ctimespec = ntfs2timespec(ni->last_mft_change_time); |
863 | stbuf->st_mtimespec = ntfs2timespec(ni->last_data_change_time); |
864 | #elif defined(HAVE_STRUCT_STAT_ST_ATIM) |
865 | stbuf->st_atim = ntfs2timespec(ni->last_access_time); |
866 | stbuf->st_ctim = ntfs2timespec(ni->last_mft_change_time); |
867 | stbuf->st_mtim = ntfs2timespec(ni->last_data_change_time); |
868 | #elif defined(HAVE_STRUCT_STAT_ST_ATIMENSEC) |
869 | { |
870 | struct timespec ts; |
871 | |
872 | ts = ntfs2timespec(ni->last_access_time); |
873 | stbuf->st_atime = ts.tv_sec; |
874 | stbuf->st_atimensec = ts.tv_nsec; |
875 | ts = ntfs2timespec(ni->last_mft_change_time); |
876 | stbuf->st_ctime = ts.tv_sec; |
877 | stbuf->st_ctimensec = ts.tv_nsec; |
878 | ts = ntfs2timespec(ni->last_data_change_time); |
879 | stbuf->st_mtime = ts.tv_sec; |
880 | stbuf->st_mtimensec = ts.tv_nsec; |
881 | } |
882 | #else |
883 | #warning "No known way to set nanoseconds in struct stat !" |
884 | { |
885 | struct timespec ts; |
886 | |
887 | ts = ntfs2timespec(ni->last_access_time); |
888 | stbuf->st_atime = ts.tv_sec; |
889 | ts = ntfs2timespec(ni->last_mft_change_time); |
890 | stbuf->st_ctime = ts.tv_sec; |
891 | ts = ntfs2timespec(ni->last_data_change_time); |
892 | stbuf->st_mtime = ts.tv_sec; |
893 | } |
894 | #endif |
895 | exit: |
896 | if (ntfs_inode_close(ni)) |
897 | set_fuse_error(&res); |
898 | free(path); |
899 | if (stream_name_len) |
900 | free(stream_name); |
901 | return res; |
902 | } |
903 | |
904 | static int ntfs_fuse_readlink(const char *org_path, char *buf, size_t buf_size) |
905 | { |
906 | char *path; |
907 | ntfschar *stream_name; |
908 | ntfs_inode *ni = NULL; |
909 | ntfs_attr *na = NULL; |
910 | INTX_FILE *intx_file = NULL; |
911 | int stream_name_len, res = 0; |
912 | |
913 | /* Get inode. */ |
914 | stream_name_len = ntfs_fuse_parse_path(org_path, &path, &stream_name); |
915 | if (stream_name_len < 0) |
916 | return stream_name_len; |
917 | if (stream_name_len > 0) { |
918 | res = -EINVAL; |
919 | goto exit; |
920 | } |
921 | ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); |
922 | if (!ni) { |
923 | res = -errno; |
924 | goto exit; |
925 | } |
926 | /* |
927 | * Reparse point : analyze as a junction point |
928 | */ |
929 | if (ni->flags & FILE_ATTR_REPARSE_POINT) { |
930 | char *target; |
931 | int attr_size; |
932 | |
933 | errno = 0; |
934 | res = 0; |
935 | target = ntfs_make_symlink(ni, ctx->abs_mnt_point, &attr_size); |
936 | if (target) { |
937 | strncpy(buf,target,buf_size); |
938 | free(target); |
939 | } else |
940 | if (errno == EOPNOTSUPP) |
941 | strcpy(buf,ntfs_bad_reparse); |
942 | else |
943 | res = -errno; |
944 | goto exit; |
945 | } |
946 | /* Sanity checks. */ |
947 | if (!(ni->flags & FILE_ATTR_SYSTEM)) { |
948 | res = -EINVAL; |
949 | goto exit; |
950 | } |
951 | na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); |
952 | if (!na) { |
953 | res = -errno; |
954 | goto exit; |
955 | } |
956 | if ((size_t)na->data_size <= sizeof(INTX_FILE_TYPES)) { |
957 | res = -EINVAL; |
958 | goto exit; |
959 | } |
960 | if ((size_t)na->data_size > sizeof(INTX_FILE_TYPES) + |
961 | sizeof(ntfschar) * PATH_MAX) { |
962 | res = -ENAMETOOLONG; |
963 | goto exit; |
964 | } |
965 | /* Receive file content. */ |
966 | intx_file = ntfs_malloc(na->data_size); |
967 | if (!intx_file) { |
968 | res = -errno; |
969 | goto exit; |
970 | } |
971 | if (ntfs_attr_pread(na, 0, na->data_size, intx_file) != na->data_size) { |
972 | res = -errno; |
973 | goto exit; |
974 | } |
975 | /* Sanity check. */ |
976 | if (intx_file->magic != INTX_SYMBOLIC_LINK) { |
977 | res = -EINVAL; |
978 | goto exit; |
979 | } |
980 | /* Convert link from unicode to local encoding. */ |
981 | if (ntfs_ucstombs(intx_file->target, (na->data_size - |
982 | offsetof(INTX_FILE, target)) / sizeof(ntfschar), |
983 | &buf, buf_size) < 0) { |
984 | res = -errno; |
985 | goto exit; |
986 | } |
987 | exit: |
988 | if (intx_file) |
989 | free(intx_file); |
990 | if (na) |
991 | ntfs_attr_close(na); |
992 | if (ntfs_inode_close(ni)) |
993 | set_fuse_error(&res); |
994 | free(path); |
995 | if (stream_name_len) |
996 | free(stream_name); |
997 | return res; |
998 | } |
999 | |
1000 | static int ntfs_fuse_filler(ntfs_fuse_fill_context_t *fill_ctx, |
1001 | const ntfschar *name, const int name_len, const int name_type, |
1002 | const s64 pos __attribute__((unused)), const MFT_REF mref, |
1003 | const unsigned dt_type __attribute__((unused))) |
1004 | { |
1005 | char *filename = NULL; |
1006 | int ret = 0; |
1007 | int filenamelen = -1; |
1008 | |
1009 | if (name_type == FILE_NAME_DOS) |
1010 | return 0; |
1011 | |
1012 | if ((filenamelen = ntfs_ucstombs(name, name_len, &filename, 0)) < 0) { |
1013 | ntfs_log_perror("Filename decoding failed (inode %llu)", |
1014 | (unsigned long long)MREF(mref)); |
1015 | return -1; |
1016 | } |
1017 | |
1018 | if (ntfs_fuse_is_named_data_stream(filename)) { |
1019 | ntfs_log_error("Unable to access '%s' (inode %llu) with " |
1020 | "current named streams access interface.\n", |
1021 | filename, (unsigned long long)MREF(mref)); |
1022 | free(filename); |
1023 | return 0; |
1024 | } else { |
1025 | struct stat st = { .st_ino = MREF(mref) }; |
1026 | |
1027 | switch (dt_type) { |
1028 | case NTFS_DT_DIR : |
1029 | st.st_mode = S_IFDIR | (0777 & ~ctx->dmask); |
1030 | break; |
1031 | case NTFS_DT_LNK : |
1032 | st.st_mode = S_IFLNK | 0777; |
1033 | break; |
1034 | case NTFS_DT_FIFO : |
1035 | st.st_mode = S_IFIFO; |
1036 | break; |
1037 | case NTFS_DT_SOCK : |
1038 | st.st_mode = S_IFSOCK; |
1039 | break; |
1040 | case NTFS_DT_BLK : |
1041 | st.st_mode = S_IFBLK; |
1042 | break; |
1043 | case NTFS_DT_CHR : |
1044 | st.st_mode = S_IFCHR; |
1045 | break; |
1046 | default : /* unexpected types shown as plain files */ |
1047 | case NTFS_DT_REG : |
1048 | st.st_mode = S_IFREG | (0777 & ~ctx->fmask); |
1049 | break; |
1050 | } |
1051 | |
1052 | #if defined(__APPLE__) || defined(__DARWIN__) |
1053 | /* |
1054 | * Returning file names larger than MAXNAMLEN (255) bytes |
1055 | * causes Darwin/Mac OS X to bug out and skip the entry. |
1056 | */ |
1057 | if (filenamelen > MAXNAMLEN) { |
1058 | ntfs_log_debug("Truncating %d byte filename to " |
1059 | "%d bytes.\n", filenamelen, MAXNAMLEN); |
1060 | ntfs_log_debug(" before: '%s'\n", filename); |
1061 | memset(filename + MAXNAMLEN, 0, filenamelen - MAXNAMLEN); |
1062 | ntfs_log_debug(" after: '%s'\n", filename); |
1063 | } |
1064 | #endif /* defined(__APPLE__) || defined(__DARWIN__) */ |
1065 | |
1066 | ret = fill_ctx->filler(fill_ctx->buf, filename, &st, 0); |
1067 | } |
1068 | |
1069 | free(filename); |
1070 | return ret; |
1071 | } |
1072 | |
1073 | #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) |
1074 | |
1075 | static int ntfs_fuse_opendir(const char *path, |
1076 | struct fuse_file_info *fi) |
1077 | { |
1078 | int res = 0; |
1079 | ntfs_inode *ni; |
1080 | int accesstype; |
1081 | struct SECURITY_CONTEXT security; |
1082 | |
1083 | if (ntfs_fuse_is_named_data_stream(path)) |
1084 | return -EINVAL; /* n/a for named data streams. */ |
1085 | |
1086 | ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); |
1087 | if (ni) { |
1088 | if (ntfs_fuse_fill_security_context(&security)) { |
1089 | if (fi->flags & O_WRONLY) |
1090 | accesstype = S_IWRITE; |
1091 | else |
1092 | if (fi->flags & O_RDWR) |
1093 | accesstype = S_IWRITE | S_IREAD; |
1094 | else |
1095 | accesstype = S_IREAD; |
1096 | /* |
1097 | * directory must be searchable |
1098 | * and requested access be allowed |
1099 | */ |
1100 | if (!strcmp(path,"/") |
1101 | ? !ntfs_allowed_dir_access(&security, |
1102 | path, ni, ni, accesstype | S_IEXEC) |
1103 | : !ntfs_allowed_dir_access(&security, path, |
1104 | (ntfs_inode*)NULL, ni, S_IEXEC) |
1105 | || !ntfs_allowed_access(&security, |
1106 | ni,accesstype)) |
1107 | res = -EACCES; |
1108 | } |
1109 | if (ntfs_inode_close(ni)) |
1110 | set_fuse_error(&res); |
1111 | } else |
1112 | res = -errno; |
1113 | return res; |
1114 | } |
1115 | |
1116 | #endif |
1117 | |
1118 | static int ntfs_fuse_readdir(const char *path, void *buf, |
1119 | fuse_fill_dir_t filler, off_t offset __attribute__((unused)), |
1120 | struct fuse_file_info *fi __attribute__((unused))) |
1121 | { |
1122 | ntfs_fuse_fill_context_t fill_ctx; |
1123 | ntfs_inode *ni; |
1124 | s64 pos = 0; |
1125 | int err = 0; |
1126 | |
1127 | fill_ctx.filler = filler; |
1128 | fill_ctx.buf = buf; |
1129 | ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); |
1130 | if (!ni) |
1131 | return -errno; |
1132 | if (ntfs_readdir(ni, &pos, &fill_ctx, |
1133 | (ntfs_filldir_t)ntfs_fuse_filler)) |
1134 | err = -errno; |
1135 | ntfs_fuse_update_times(ni, NTFS_UPDATE_ATIME); |
1136 | if (ntfs_inode_close(ni)) |
1137 | set_fuse_error(&err); |
1138 | return err; |
1139 | } |
1140 | |
1141 | static int ntfs_fuse_open(const char *org_path, |
1142 | #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) |
1143 | struct fuse_file_info *fi) |
1144 | #else |
1145 | struct fuse_file_info *fi __attribute__((unused))) |
1146 | #endif |
1147 | { |
1148 | ntfs_inode *ni; |
1149 | ntfs_attr *na; |
1150 | int res = 0; |
1151 | char *path = NULL; |
1152 | ntfschar *stream_name; |
1153 | int stream_name_len; |
1154 | #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) |
1155 | int accesstype; |
1156 | struct SECURITY_CONTEXT security; |
1157 | #endif |
1158 | |
1159 | stream_name_len = ntfs_fuse_parse_path(org_path, &path, &stream_name); |
1160 | if (stream_name_len < 0) |
1161 | return stream_name_len; |
1162 | ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); |
1163 | if (ni) { |
1164 | na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_len); |
1165 | if (na) { |
1166 | #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) |
1167 | if (ntfs_fuse_fill_security_context(&security)) { |
1168 | if (fi->flags & O_WRONLY) |
1169 | accesstype = S_IWRITE; |
1170 | else |
1171 | if (fi->flags & O_RDWR) |
1172 | accesstype = S_IWRITE | S_IREAD; |
1173 | else |
1174 | accesstype = S_IREAD; |
1175 | /* |
1176 | * directory must be searchable |
1177 | * and requested access allowed |
1178 | */ |
1179 | if (!ntfs_allowed_dir_access(&security, |
1180 | path,(ntfs_inode*)NULL,ni,S_IEXEC) |
1181 | || !ntfs_allowed_access(&security, |
1182 | ni,accesstype)) |
1183 | res = -EACCES; |
1184 | } |
1185 | #endif |
1186 | if ((res >= 0) |
1187 | && (fi->flags & (O_WRONLY | O_RDWR))) { |
1188 | /* mark a future need to compress the last chunk */ |
1189 | if (na->data_flags & ATTR_COMPRESSION_MASK) |
1190 | fi->fh |= CLOSE_COMPRESSED; |
1191 | #ifdef HAVE_SETXATTR /* extended attributes interface required */ |
1192 | /* mark a future need to fixup encrypted inode */ |
1193 | if (ctx->efs_raw |
1194 | && !(na->data_flags & ATTR_IS_ENCRYPTED) |
1195 | && (ni->flags & FILE_ATTR_ENCRYPTED)) |
1196 | fi->fh |= CLOSE_ENCRYPTED; |
1197 | #endif /* HAVE_SETXATTR */ |
1198 | /* mark a future need to update the mtime */ |
1199 | if (ctx->dmtime) |
1200 | fi->fh |= CLOSE_DMTIME; |
1201 | /* deny opening metadata files for writing */ |
1202 | if (ni->mft_no < FILE_first_user) |
1203 | res = -EPERM; |
1204 | } |
1205 | ntfs_attr_close(na); |
1206 | } else |
1207 | res = -errno; |
1208 | if (ntfs_inode_close(ni)) |
1209 | set_fuse_error(&res); |
1210 | } else |
1211 | res = -errno; |
1212 | free(path); |
1213 | if (stream_name_len) |
1214 | free(stream_name); |
1215 | return res; |
1216 | } |
1217 | |
1218 | static int ntfs_fuse_read(const char *org_path, char *buf, size_t size, |
1219 | off_t offset, struct fuse_file_info *fi __attribute__((unused))) |
1220 | { |
1221 | ntfs_inode *ni = NULL; |
1222 | ntfs_attr *na = NULL; |
1223 | char *path = NULL; |
1224 | ntfschar *stream_name; |
1225 | int stream_name_len, res; |
1226 | s64 total = 0; |
1227 | s64 max_read; |
1228 | |
1229 | if (!size) |
1230 | return 0; |
1231 | |
1232 | stream_name_len = ntfs_fuse_parse_path(org_path, &path, &stream_name); |
1233 | if (stream_name_len < 0) |
1234 | return stream_name_len; |
1235 | ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); |
1236 | if (!ni) { |
1237 | res = -errno; |
1238 | goto exit; |
1239 | } |
1240 | na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_len); |
1241 | if (!na) { |
1242 | res = -errno; |
1243 | goto exit; |
1244 | } |
1245 | max_read = na->data_size; |
1246 | #ifdef HAVE_SETXATTR /* extended attributes interface required */ |
1247 | /* limit reads at next 512 byte boundary for encrypted attributes */ |
1248 | if (ctx->efs_raw |
1249 | && max_read |
1250 | && (na->data_flags & ATTR_IS_ENCRYPTED) |
1251 | && NAttrNonResident(na)) { |
1252 | max_read = ((na->data_size+511) & ~511) + 2; |
1253 | } |
1254 | #endif /* HAVE_SETXATTR */ |
1255 | if (offset + (off_t)size > max_read) { |
1256 | if (max_read < offset) |
1257 | goto ok; |
1258 | size = max_read - offset; |
1259 | } |
1260 | while (size > 0) { |
1261 | s64 ret = ntfs_attr_pread(na, offset, size, buf + total); |
1262 | if (ret != (s64)size) |
1263 | ntfs_log_perror("ntfs_attr_pread error reading '%s' at " |
1264 | "offset %lld: %lld <> %lld", org_path, |
1265 | (long long)offset, (long long)size, (long long)ret); |
1266 | if (ret <= 0 || ret > (s64)size) { |
1267 | res = (ret < 0) ? -errno : -EIO; |
1268 | goto exit; |
1269 | } |
1270 | size -= ret; |
1271 | offset += ret; |
1272 | total += ret; |
1273 | } |
1274 | ok: |
1275 | ntfs_fuse_update_times(na->ni, NTFS_UPDATE_ATIME); |
1276 | res = total; |
1277 | exit: |
1278 | if (na) |
1279 | ntfs_attr_close(na); |
1280 | if (ntfs_inode_close(ni)) |
1281 | set_fuse_error(&res); |
1282 | free(path); |
1283 | if (stream_name_len) |
1284 | free(stream_name); |
1285 | return res; |
1286 | } |
1287 | |
1288 | static int ntfs_fuse_write(const char *org_path, const char *buf, size_t size, |
1289 | off_t offset, struct fuse_file_info *fi __attribute__((unused))) |
1290 | { |
1291 | ntfs_inode *ni = NULL; |
1292 | ntfs_attr *na = NULL; |
1293 | char *path = NULL; |
1294 | ntfschar *stream_name; |
1295 | int stream_name_len, res, total = 0; |
1296 | |
1297 | stream_name_len = ntfs_fuse_parse_path(org_path, &path, &stream_name); |
1298 | if (stream_name_len < 0) { |
1299 | res = stream_name_len; |
1300 | goto out; |
1301 | } |
1302 | ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); |
1303 | if (!ni) { |
1304 | res = -errno; |
1305 | goto exit; |
1306 | } |
1307 | na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_len); |
1308 | if (!na) { |
1309 | res = -errno; |
1310 | goto exit; |
1311 | } |
1312 | while (size) { |
1313 | s64 ret = ntfs_attr_pwrite(na, offset, size, buf + total); |
1314 | if (ret <= 0) { |
1315 | res = -errno; |
1316 | goto exit; |
1317 | } |
1318 | size -= ret; |
1319 | offset += ret; |
1320 | total += ret; |
1321 | } |
1322 | res = total; |
1323 | if ((res > 0) |
1324 | && (!ctx->dmtime |
1325 | || (le64_to_cpu(ntfs_current_time()) |
1326 | - le64_to_cpu(ni->last_data_change_time)) > ctx->dmtime)) |
1327 | ntfs_fuse_update_times(na->ni, NTFS_UPDATE_MCTIME); |
1328 | exit: |
1329 | if (na) |
1330 | ntfs_attr_close(na); |
1331 | if (total) |
1332 | set_archive(ni); |
1333 | if (ntfs_inode_close(ni)) |
1334 | set_fuse_error(&res); |
1335 | free(path); |
1336 | if (stream_name_len) |
1337 | free(stream_name); |
1338 | out: |
1339 | return res; |
1340 | } |
1341 | |
1342 | static int ntfs_fuse_release(const char *org_path, |
1343 | struct fuse_file_info *fi) |
1344 | { |
1345 | ntfs_inode *ni = NULL; |
1346 | ntfs_attr *na = NULL; |
1347 | char *path = NULL; |
1348 | ntfschar *stream_name; |
1349 | int stream_name_len, res; |
1350 | |
1351 | /* Only for marked descriptors there is something to do */ |
1352 | if (!(fi->fh & (CLOSE_COMPRESSED | CLOSE_ENCRYPTED | CLOSE_DMTIME))) { |
1353 | res = 0; |
1354 | goto out; |
1355 | } |
1356 | stream_name_len = ntfs_fuse_parse_path(org_path, &path, &stream_name); |
1357 | if (stream_name_len < 0) { |
1358 | res = stream_name_len; |
1359 | goto out; |
1360 | } |
1361 | ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); |
1362 | if (!ni) { |
1363 | res = -errno; |
1364 | goto exit; |
1365 | } |
1366 | na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_len); |
1367 | if (!na) { |
1368 | res = -errno; |
1369 | goto exit; |
1370 | } |
1371 | res = 0; |
1372 | if (fi->fh & CLOSE_DMTIME) |
1373 | ntfs_inode_update_times(na->ni,NTFS_UPDATE_MCTIME); |
1374 | if (fi->fh & CLOSE_COMPRESSED) |
1375 | res = ntfs_attr_pclose(na); |
1376 | #ifdef HAVE_SETXATTR /* extended attributes interface required */ |
1377 | if (fi->fh & CLOSE_ENCRYPTED) |
1378 | res = ntfs_efs_fixup_attribute(NULL, na); |
1379 | #endif /* HAVE_SETXATTR */ |
1380 | exit: |
1381 | if (na) |
1382 | ntfs_attr_close(na); |
1383 | if (ntfs_inode_close(ni)) |
1384 | set_fuse_error(&res); |
1385 | free(path); |
1386 | if (stream_name_len) |
1387 | free(stream_name); |
1388 | out: |
1389 | return res; |
1390 | } |
1391 | |
1392 | /* |
1393 | * Common part for truncate() and ftruncate() |
1394 | */ |
1395 | |
1396 | static int ntfs_fuse_trunc(const char *org_path, off_t size, |
1397 | #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) |
1398 | BOOL chkwrite) |
1399 | #else |
1400 | BOOL chkwrite __attribute__((unused))) |
1401 | #endif |
1402 | { |
1403 | ntfs_inode *ni = NULL; |
1404 | ntfs_attr *na = NULL; |
1405 | int res; |
1406 | char *path = NULL; |
1407 | ntfschar *stream_name; |
1408 | int stream_name_len; |
1409 | s64 oldsize; |
1410 | #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) |
1411 | struct SECURITY_CONTEXT security; |
1412 | #endif |
1413 | |
1414 | stream_name_len = ntfs_fuse_parse_path(org_path, &path, &stream_name); |
1415 | if (stream_name_len < 0) |
1416 | return stream_name_len; |
1417 | ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); |
1418 | if (!ni) |
1419 | goto exit; |
1420 | /* deny truncating metadata files */ |
1421 | if (ni->mft_no < FILE_first_user) { |
1422 | errno = EPERM; |
1423 | goto exit; |
1424 | } |
1425 | |
1426 | na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_len); |
1427 | if (!na) |
1428 | goto exit; |
1429 | #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) |
1430 | /* |
1431 | * JPA deny truncation if cannot search in parent directory |
1432 | * or cannot write to file (already checked for ftruncate()) |
1433 | */ |
1434 | if (ntfs_fuse_fill_security_context(&security) |
1435 | && (!ntfs_allowed_dir_access(&security, path, |
1436 | (ntfs_inode*)NULL, ni, S_IEXEC) |
1437 | || (chkwrite |
1438 | && !ntfs_allowed_access(&security, ni, S_IWRITE)))) { |
1439 | errno = EACCES; |
1440 | goto exit; |
1441 | } |
1442 | #endif |
1443 | /* |
1444 | * For compressed files, upsizing is done by inserting a final |
1445 | * zero, which is optimized as creating a hole when possible. |
1446 | */ |
1447 | oldsize = na->data_size; |
1448 | if ((na->data_flags & ATTR_COMPRESSION_MASK) |
1449 | && (size > na->initialized_size)) { |
1450 | char zero = 0; |
1451 | if (ntfs_attr_pwrite(na, size - 1, 1, &zero) <= 0) |
1452 | goto exit; |
1453 | } else |
1454 | if (ntfs_attr_truncate(na, size)) |
1455 | goto exit; |
1456 | if (oldsize != size) |
1457 | set_archive(ni); |
1458 | |
1459 | ntfs_fuse_update_times(na->ni, NTFS_UPDATE_MCTIME); |
1460 | errno = 0; |
1461 | exit: |
1462 | res = -errno; |
1463 | ntfs_attr_close(na); |
1464 | if (ntfs_inode_close(ni)) |
1465 | set_fuse_error(&res); |
1466 | free(path); |
1467 | if (stream_name_len) |
1468 | free(stream_name); |
1469 | return res; |
1470 | } |
1471 | |
1472 | static int ntfs_fuse_truncate(const char *org_path, off_t size) |
1473 | { |
1474 | return ntfs_fuse_trunc(org_path, size, TRUE); |
1475 | } |
1476 | |
1477 | static int ntfs_fuse_ftruncate(const char *org_path, off_t size, |
1478 | struct fuse_file_info *fi __attribute__((unused))) |
1479 | { |
1480 | /* |
1481 | * in ->ftruncate() the file handle is guaranteed |
1482 | * to have been opened for write. |
1483 | */ |
1484 | return (ntfs_fuse_trunc(org_path, size, FALSE)); |
1485 | } |
1486 | |
1487 | static int ntfs_fuse_chmod(const char *path, |
1488 | mode_t mode) |
1489 | { |
1490 | int res = 0; |
1491 | ntfs_inode *ni; |
1492 | struct SECURITY_CONTEXT security; |
1493 | |
1494 | if (ntfs_fuse_is_named_data_stream(path)) |
1495 | return -EINVAL; /* n/a for named data streams. */ |
1496 | |
1497 | /* JPA return unsupported if no user mapping has been defined */ |
1498 | if (!ntfs_fuse_fill_security_context(&security)) { |
1499 | if (ctx->silent) |
1500 | res = 0; |
1501 | else |
1502 | res = -EOPNOTSUPP; |
1503 | } else { |
1504 | #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) |
1505 | /* parent directory must be executable */ |
1506 | if (ntfs_allowed_dir_access(&security,path, |
1507 | (ntfs_inode*)NULL,(ntfs_inode*)NULL,S_IEXEC)) { |
1508 | #endif |
1509 | ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); |
1510 | if (!ni) |
1511 | res = -errno; |
1512 | else { |
1513 | if (ntfs_set_mode(&security,ni,mode)) |
1514 | res = -errno; |
1515 | else |
1516 | ntfs_fuse_update_times(ni, NTFS_UPDATE_CTIME); |
1517 | NInoSetDirty(ni); |
1518 | if (ntfs_inode_close(ni)) |
1519 | set_fuse_error(&res); |
1520 | } |
1521 | #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) |
1522 | } else |
1523 | res = -errno; |
1524 | #endif |
1525 | } |
1526 | return res; |
1527 | } |
1528 | |
1529 | static int ntfs_fuse_chown(const char *path, uid_t uid, gid_t gid) |
1530 | { |
1531 | ntfs_inode *ni; |
1532 | int res; |
1533 | struct SECURITY_CONTEXT security; |
1534 | |
1535 | if (ntfs_fuse_is_named_data_stream(path)) |
1536 | return -EINVAL; /* n/a for named data streams. */ |
1537 | if (!ntfs_fuse_fill_security_context(&security)) { |
1538 | if (ctx->silent) |
1539 | return 0; |
1540 | if (uid == ctx->uid && gid == ctx->gid) |
1541 | return 0; |
1542 | return -EOPNOTSUPP; |
1543 | } else { |
1544 | res = 0; |
1545 | if (((int)uid != -1) || ((int)gid != -1)) { |
1546 | #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) |
1547 | /* parent directory must be executable */ |
1548 | |
1549 | if (ntfs_allowed_dir_access(&security,path, |
1550 | (ntfs_inode*)NULL,(ntfs_inode*)NULL,S_IEXEC)) { |
1551 | #endif |
1552 | ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); |
1553 | if (!ni) |
1554 | res = -errno; |
1555 | else { |
1556 | if (ntfs_set_owner(&security, |
1557 | ni,uid,gid)) |
1558 | res = -errno; |
1559 | else |
1560 | ntfs_fuse_update_times(ni, NTFS_UPDATE_CTIME); |
1561 | if (ntfs_inode_close(ni)) |
1562 | set_fuse_error(&res); |
1563 | } |
1564 | #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) |
1565 | } else |
1566 | res = -errno; |
1567 | #endif |
1568 | } |
1569 | } |
1570 | return (res); |
1571 | } |
1572 | |
1573 | #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) |
1574 | |
1575 | static int ntfs_fuse_access(const char *path, int type) |
1576 | { |
1577 | int res = 0; |
1578 | int mode; |
1579 | ntfs_inode *ni; |
1580 | struct SECURITY_CONTEXT security; |
1581 | |
1582 | if (ntfs_fuse_is_named_data_stream(path)) |
1583 | return -EINVAL; /* n/a for named data streams. */ |
1584 | |
1585 | /* JPA return unsupported if no user mapping has been defined */ |
1586 | if (!ntfs_fuse_fill_security_context(&security)) { |
1587 | if (ctx->silent) |
1588 | res = 0; |
1589 | else |
1590 | res = -EOPNOTSUPP; |
1591 | } else { |
1592 | /* parent directory must be seachable */ |
1593 | if (ntfs_allowed_dir_access(&security,path,(ntfs_inode*)NULL, |
1594 | (ntfs_inode*)NULL,S_IEXEC)) { |
1595 | ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); |
1596 | if (!ni) { |
1597 | res = -errno; |
1598 | } else { |
1599 | mode = 0; |
1600 | if (type & (X_OK | W_OK | R_OK)) { |
1601 | if (type & X_OK) mode += S_IEXEC; |
1602 | if (type & W_OK) mode += S_IWRITE; |
1603 | if (type & R_OK) mode += S_IREAD; |
1604 | if (!ntfs_allowed_access(&security, |
1605 | ni, mode)) |
1606 | res = -errno; |
1607 | } |
1608 | if (ntfs_inode_close(ni)) |
1609 | set_fuse_error(&res); |
1610 | } |
1611 | } else |
1612 | res = -errno; |
1613 | } |
1614 | return (res); |
1615 | } |
1616 | |
1617 | #endif |
1618 | |
1619 | static int ntfs_fuse_create(const char *org_path, mode_t typemode, dev_t dev, |
1620 | const char *target, struct fuse_file_info *fi) |
1621 | { |
1622 | char *name; |
1623 | ntfschar *uname = NULL, *utarget = NULL; |
1624 | ntfs_inode *dir_ni = NULL, *ni; |
1625 | char *dir_path; |
1626 | le32 securid; |
1627 | char *path; |
1628 | gid_t gid; |
1629 | mode_t dsetgid; |
1630 | ntfschar *stream_name; |
1631 | int stream_name_len; |
1632 | mode_t type = typemode & ~07777; |
1633 | mode_t perm; |
1634 | struct SECURITY_CONTEXT security; |
1635 | int res = 0, uname_len, utarget_len; |
1636 | |
1637 | dir_path = strdup(org_path); |
1638 | if (!dir_path) |
1639 | return -errno; |
1640 | /* Generate unicode filename. */ |
1641 | name = strrchr(dir_path, '/'); |
1642 | name++; |
1643 | uname_len = ntfs_mbstoucs(name, &uname); |
1644 | if ((uname_len < 0) |
1645 | || (ctx->windows_names |
1646 | && ntfs_forbidden_chars(uname,uname_len))) { |
1647 | res = -errno; |
1648 | goto exit; |
1649 | } |
1650 | stream_name_len = ntfs_fuse_parse_path(org_path, |
1651 | &path, &stream_name); |
1652 | /* stream name validity has been checked previously */ |
1653 | if (stream_name_len < 0) { |
1654 | res = stream_name_len; |
1655 | goto exit; |
1656 | } |
1657 | /* Open parent directory. */ |
1658 | *--name = 0; |
1659 | dir_ni = ntfs_pathname_to_inode(ctx->vol, NULL, dir_path); |
1660 | if (!dir_ni) { |
1661 | free(path); |
1662 | res = -errno; |
1663 | goto exit; |
1664 | } |
1665 | #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) |
1666 | /* make sure parent directory is writeable and executable */ |
1667 | if (!ntfs_fuse_fill_security_context(&security) |
1668 | || ntfs_allowed_create(&security, |
1669 | dir_ni, &gid, &dsetgid)) { |
1670 | #else |
1671 | ntfs_fuse_fill_security_context(&security); |
1672 | ntfs_allowed_create(&security, dir_ni, &gid, &dsetgid); |
1673 | #endif |
1674 | if (S_ISDIR(type)) |
1675 | perm = (typemode & ~ctx->dmask & 0777) |
1676 | | (dsetgid & S_ISGID); |
1677 | else |
1678 | perm = typemode & ~ctx->fmask & 0777; |
1679 | /* |
1680 | * Try to get a security id available for |
1681 | * file creation (from inheritance or argument). |
1682 | * This is not possible for NTFS 1.x, and we will |
1683 | * have to build a security attribute later. |
1684 | */ |
1685 | if (!ctx->security.mapping[MAPUSERS]) |
1686 | securid = 0; |
1687 | else |
1688 | if (ctx->inherit) |
1689 | securid = ntfs_inherited_id(&security, |
1690 | dir_ni, S_ISDIR(type)); |
1691 | else |
1692 | #if POSIXACLS |
1693 | securid = ntfs_alloc_securid(&security, |
1694 | security.uid, gid, |
1695 | dir_ni, perm, S_ISDIR(type)); |
1696 | #else |
1697 | securid = ntfs_alloc_securid(&security, |
1698 | security.uid, gid, |
1699 | perm & ~security.umask, S_ISDIR(type)); |
1700 | #endif |
1701 | /* Create object specified in @type. */ |
1702 | switch (type) { |
1703 | case S_IFCHR: |
1704 | case S_IFBLK: |
1705 | ni = ntfs_create_device(dir_ni, securid, |
1706 | uname, uname_len, type, dev); |
1707 | break; |
1708 | case S_IFLNK: |
1709 | utarget_len = ntfs_mbstoucs(target, &utarget); |
1710 | if (utarget_len < 0) { |
1711 | res = -errno; |
1712 | goto exit; |
1713 | } |
1714 | ni = ntfs_create_symlink(dir_ni, securid, |
1715 | uname, uname_len, |
1716 | utarget, utarget_len); |
1717 | break; |
1718 | default: |
1719 | ni = ntfs_create(dir_ni, securid, uname, |
1720 | uname_len, type); |
1721 | break; |
1722 | } |
1723 | if (ni) { |
1724 | /* |
1725 | * set the security attribute if a security id |
1726 | * could not be allocated (eg NTFS 1.x) |
1727 | */ |
1728 | if (ctx->security.mapping[MAPUSERS]) { |
1729 | #if POSIXACLS |
1730 | if (!securid |
1731 | && ntfs_set_inherited_posix(&security, ni, |
1732 | security.uid, gid, |
1733 | dir_ni, perm) < 0) |
1734 | set_fuse_error(&res); |
1735 | #else |
1736 | if (!securid |
1737 | && ntfs_set_owner_mode(&security, ni, |
1738 | security.uid, gid, |
1739 | perm & ~security.umask) < 0) |
1740 | set_fuse_error(&res); |
1741 | #endif |
1742 | } |
1743 | set_archive(ni); |
1744 | /* mark a need to compress the end of file */ |
1745 | if (fi && (ni->flags & FILE_ATTR_COMPRESSED)) { |
1746 | fi->fh |= CLOSE_COMPRESSED; |
1747 | } |
1748 | #ifdef HAVE_SETXATTR /* extended attributes interface required */ |
1749 | /* mark a future need to fixup encrypted inode */ |
1750 | if (fi |
1751 | && ctx->efs_raw |
1752 | && (ni->flags & FILE_ATTR_ENCRYPTED)) |
1753 | fi->fh |= CLOSE_ENCRYPTED; |
1754 | #endif /* HAVE_SETXATTR */ |
1755 | /* mark a need to update the mtime */ |
1756 | if (fi && ctx->dmtime) |
1757 | fi->fh |= CLOSE_DMTIME; |
1758 | NInoSetDirty(ni); |
1759 | /* |
1760 | * closing ni requires access to dir_ni to |
1761 | * synchronize the index, avoid double opening. |
1762 | */ |
1763 | if (ntfs_inode_close_in_dir(ni, dir_ni)) |
1764 | set_fuse_error(&res); |
1765 | ntfs_fuse_update_times(dir_ni, NTFS_UPDATE_MCTIME); |
1766 | } else |
1767 | res = -errno; |
1768 | #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) |
1769 | } else |
1770 | res = -errno; |
1771 | #endif |
1772 | free(path); |
1773 | |
1774 | exit: |
1775 | free(uname); |
1776 | if (ntfs_inode_close(dir_ni)) |
1777 | set_fuse_error(&res); |
1778 | if (utarget) |
1779 | free(utarget); |
1780 | free(dir_path); |
1781 | return res; |
1782 | } |
1783 | |
1784 | static int ntfs_fuse_create_stream(const char *path, |
1785 | ntfschar *stream_name, const int stream_name_len, |
1786 | struct fuse_file_info *fi) |
1787 | { |
1788 | ntfs_inode *ni; |
1789 | int res = 0; |
1790 | |
1791 | ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); |
1792 | if (!ni) { |
1793 | res = -errno; |
1794 | if (res == -ENOENT) { |
1795 | /* |
1796 | * If such file does not exist, create it and try once |
1797 | * again to add stream to it. |
1798 | * Note : no fuse_file_info for creation of main file |
1799 | */ |
1800 | res = ntfs_fuse_create(path, S_IFREG, 0, NULL, |
1801 | (struct fuse_file_info*)NULL); |
1802 | if (!res) |
1803 | return ntfs_fuse_create_stream(path, |
1804 | stream_name, stream_name_len,fi); |
1805 | else |
1806 | res = -errno; |
1807 | } |
1808 | return res; |
1809 | } |
1810 | if (ntfs_attr_add(ni, AT_DATA, stream_name, stream_name_len, NULL, 0)) |
1811 | res = -errno; |
1812 | else |
1813 | set_archive(ni); |
1814 | |
1815 | if ((res >= 0) |
1816 | && fi |
1817 | && (fi->flags & (O_WRONLY | O_RDWR))) { |
1818 | /* mark a future need to compress the last block */ |
1819 | if (ni->flags & FILE_ATTR_COMPRESSED) |
1820 | fi->fh |= CLOSE_COMPRESSED; |
1821 | #ifdef HAVE_SETXATTR /* extended attributes interface required */ |
1822 | /* mark a future need to fixup encrypted inode */ |
1823 | if (ctx->efs_raw |
1824 | && (ni->flags & FILE_ATTR_ENCRYPTED)) |
1825 | fi->fh |= CLOSE_ENCRYPTED; |
1826 | #endif /* HAVE_SETXATTR */ |
1827 | if (ctx->dmtime) |
1828 | fi->fh |= CLOSE_DMTIME; |
1829 | } |
1830 | |
1831 | if (ntfs_inode_close(ni)) |
1832 | set_fuse_error(&res); |
1833 | return res; |
1834 | } |
1835 | |
1836 | static int ntfs_fuse_mknod_common(const char *org_path, mode_t mode, dev_t dev, |
1837 | struct fuse_file_info *fi) |
1838 | { |
1839 | char *path = NULL; |
1840 | ntfschar *stream_name; |
1841 | int stream_name_len; |
1842 | int res = 0; |
1843 | |
1844 | stream_name_len = ntfs_fuse_parse_path(org_path, &path, &stream_name); |
1845 | if (stream_name_len < 0) |
1846 | return stream_name_len; |
1847 | if (stream_name_len |
1848 | && (!S_ISREG(mode) |
1849 | || (ctx->windows_names |
1850 | && ntfs_forbidden_chars(stream_name,stream_name_len)))) { |
1851 | res = -EINVAL; |
1852 | goto exit; |
1853 | } |
1854 | if (!stream_name_len) |
1855 | res = ntfs_fuse_create(path, mode & (S_IFMT | 07777), dev, |
1856 | NULL,fi); |
1857 | else |
1858 | res = ntfs_fuse_create_stream(path, stream_name, |
1859 | stream_name_len,fi); |
1860 | exit: |
1861 | free(path); |
1862 | if (stream_name_len) |
1863 | free(stream_name); |
1864 | return res; |
1865 | } |
1866 | |
1867 | static int ntfs_fuse_mknod(const char *path, mode_t mode, dev_t dev) |
1868 | { |
1869 | return ntfs_fuse_mknod_common(path, mode, dev, |
1870 | (struct fuse_file_info*)NULL); |
1871 | } |
1872 | |
1873 | static int ntfs_fuse_create_file(const char *path, mode_t mode, |
1874 | struct fuse_file_info *fi) |
1875 | { |
1876 | return ntfs_fuse_mknod_common(path, mode, 0, fi); |
1877 | } |
1878 | |
1879 | static int ntfs_fuse_symlink(const char *to, const char *from) |
1880 | { |
1881 | if (ntfs_fuse_is_named_data_stream(from)) |
1882 | return -EINVAL; /* n/a for named data streams. */ |
1883 | return ntfs_fuse_create(from, S_IFLNK, 0, to, |
1884 | (struct fuse_file_info*)NULL); |
1885 | } |
1886 | |
1887 | static int ntfs_fuse_link(const char *old_path, const char *new_path) |
1888 | { |
1889 | char *name; |
1890 | ntfschar *uname = NULL; |
1891 | ntfs_inode *dir_ni = NULL, *ni; |
1892 | char *path; |
1893 | int res = 0, uname_len; |
1894 | #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) |
1895 | BOOL samedir; |
1896 | struct SECURITY_CONTEXT security; |
1897 | #endif |
1898 | |
1899 | if (ntfs_fuse_is_named_data_stream(old_path)) |
1900 | return -EINVAL; /* n/a for named data streams. */ |
1901 | if (ntfs_fuse_is_named_data_stream(new_path)) |
1902 | return -EINVAL; /* n/a for named data streams. */ |
1903 | path = strdup(new_path); |
1904 | if (!path) |
1905 | return -errno; |
1906 | /* Open file for which create hard link. */ |
1907 | ni = ntfs_pathname_to_inode(ctx->vol, NULL, old_path); |
1908 | if (!ni) { |
1909 | res = -errno; |
1910 | goto exit; |
1911 | } |
1912 | |
1913 | /* Generate unicode filename. */ |
1914 | name = strrchr(path, '/'); |
1915 | name++; |
1916 | uname_len = ntfs_mbstoucs(name, &uname); |
1917 | if ((uname_len < 0) |
1918 | || (ctx->windows_names |
1919 | && ntfs_forbidden_chars(uname,uname_len))) { |
1920 | res = -errno; |
1921 | goto exit; |
1922 | } |
1923 | /* Open parent directory. */ |
1924 | *--name = 0; |
1925 | dir_ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); |
1926 | if (!dir_ni) { |
1927 | res = -errno; |
1928 | goto exit; |
1929 | } |
1930 | |
1931 | #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) |
1932 | samedir = !strncmp(old_path, path, strlen(path)) |
1933 | && (old_path[strlen(path)] == '/'); |
1934 | /* JPA make sure the parent directories are writeable */ |
1935 | if (ntfs_fuse_fill_security_context(&security) |
1936 | && ((!samedir && !ntfs_allowed_dir_access(&security,old_path, |
1937 | (ntfs_inode*)NULL,ni,S_IWRITE + S_IEXEC)) |
1938 | || !ntfs_allowed_access(&security,dir_ni,S_IWRITE + S_IEXEC))) |
1939 | res = -EACCES; |
1940 | else |
1941 | #endif |
1942 | { |
1943 | if (ntfs_link(ni, dir_ni, uname, uname_len)) { |
1944 | res = -errno; |
1945 | goto exit; |
1946 | } |
1947 | |
1948 | set_archive(ni); |
1949 | ntfs_fuse_update_times(ni, NTFS_UPDATE_CTIME); |
1950 | ntfs_fuse_update_times(dir_ni, NTFS_UPDATE_MCTIME); |
1951 | } |
1952 | exit: |
1953 | /* |
1954 | * Must close dir_ni first otherwise ntfs_inode_sync_file_name(ni) |
1955 | * may fail because ni may not be in parent's index on the disk yet. |
1956 | */ |
1957 | if (ntfs_inode_close(dir_ni)) |
1958 | set_fuse_error(&res); |
1959 | if (ntfs_inode_close(ni)) |
1960 | set_fuse_error(&res); |
1961 | free(uname); |
1962 | free(path); |
1963 | return res; |
1964 | } |
1965 | |
1966 | static int ntfs_fuse_rm(const char *org_path) |
1967 | { |
1968 | char *name; |
1969 | ntfschar *uname = NULL; |
1970 | ntfs_inode *dir_ni = NULL, *ni; |
1971 | char *path; |
1972 | int res = 0, uname_len; |
1973 | #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) |
1974 | struct SECURITY_CONTEXT security; |
1975 | #endif |
1976 | |
1977 | path = strdup(org_path); |
1978 | if (!path) |
1979 | return -errno; |
1980 | /* Open object for delete. */ |
1981 | ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); |
1982 | if (!ni) { |
1983 | res = -errno; |
1984 | goto exit; |
1985 | } |
1986 | /* deny unlinking metadata files */ |
1987 | if (ni->mft_no < FILE_first_user) { |
1988 | errno = EPERM; |
1989 | res = -errno; |
1990 | goto exit; |
1991 | } |
1992 | |
1993 | /* Generate unicode filename. */ |
1994 | name = strrchr(path, '/'); |
1995 | name++; |
1996 | uname_len = ntfs_mbstoucs(name, &uname); |
1997 | if (uname_len < 0) { |
1998 | res = -errno; |
1999 | goto exit; |
2000 | } |
2001 | /* Open parent directory. */ |
2002 | *--name = 0; |
2003 | dir_ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); |
2004 | if (!dir_ni) { |
2005 | res = -errno; |
2006 | goto exit; |
2007 | } |
2008 | |
2009 | #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) |
2010 | /* JPA deny unlinking if directory is not writable and executable */ |
2011 | if (!ntfs_fuse_fill_security_context(&security) |
2012 | || ntfs_allowed_dir_access(&security, org_path, dir_ni, ni, |
2013 | S_IEXEC + S_IWRITE + S_ISVTX)) { |
2014 | #endif |
2015 | if (ntfs_delete(ctx->vol, org_path, ni, dir_ni, |
2016 | uname, uname_len)) |
2017 | res = -errno; |
2018 | /* ntfs_delete() always closes ni and dir_ni */ |
2019 | ni = dir_ni = NULL; |
2020 | #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) |
2021 | } else |
2022 | res = -EACCES; |
2023 | #endif |
2024 | exit: |
2025 | if (ntfs_inode_close(dir_ni)) |
2026 | set_fuse_error(&res); |
2027 | if (ntfs_inode_close(ni)) |
2028 | set_fuse_error(&res); |
2029 | free(uname); |
2030 | free(path); |
2031 | return res; |
2032 | } |
2033 | |
2034 | static int ntfs_fuse_rm_stream(const char *path, ntfschar *stream_name, |
2035 | const int stream_name_len) |
2036 | { |
2037 | ntfs_inode *ni; |
2038 | int res = 0; |
2039 | |
2040 | ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); |
2041 | if (!ni) |
2042 | return -errno; |
2043 | |
2044 | if (ntfs_attr_remove(ni, AT_DATA, stream_name, stream_name_len)) |
2045 | res = -errno; |
2046 | |
2047 | if (ntfs_inode_close(ni)) |
2048 | set_fuse_error(&res); |
2049 | return res; |
2050 | } |
2051 | |
2052 | static int ntfs_fuse_unlink(const char *org_path) |
2053 | { |
2054 | char *path = NULL; |
2055 | ntfschar *stream_name; |
2056 | int stream_name_len; |
2057 | int res = 0; |
2058 | #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) |
2059 | struct SECURITY_CONTEXT security; |
2060 | #endif |
2061 | |
2062 | stream_name_len = ntfs_fuse_parse_path(org_path, &path, &stream_name); |
2063 | if (stream_name_len < 0) |
2064 | return stream_name_len; |
2065 | if (!stream_name_len) |
2066 | res = ntfs_fuse_rm(path); |
2067 | else { |
2068 | #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) |
2069 | /* |
2070 | * JPA deny unlinking stream if directory is not |
2071 | * writable and executable (debatable) |
2072 | */ |
2073 | if (!ntfs_fuse_fill_security_context(&security) |
2074 | || ntfs_allowed_dir_access(&security, path, |
2075 | (ntfs_inode*)NULL, (ntfs_inode*)NULL, |
2076 | S_IEXEC + S_IWRITE + S_ISVTX)) |
2077 | res = ntfs_fuse_rm_stream(path, stream_name, |
2078 | stream_name_len); |
2079 | else |
2080 | res = -errno; |
2081 | #else |
2082 | res = ntfs_fuse_rm_stream(path, stream_name, stream_name_len); |
2083 | #endif |
2084 | } |
2085 | free(path); |
2086 | if (stream_name_len) |
2087 | free(stream_name); |
2088 | return res; |
2089 | } |
2090 | |
2091 | static int ntfs_fuse_safe_rename(const char *old_path, |
2092 | const char *new_path, |
2093 | const char *tmp) |
2094 | { |
2095 | int ret; |
2096 | |
2097 | ntfs_log_trace("Entering\n"); |
2098 | |
2099 | ret = ntfs_fuse_link(new_path, tmp); |
2100 | if (ret) |
2101 | return ret; |
2102 | |
2103 | ret = ntfs_fuse_unlink(new_path); |
2104 | if (!ret) { |
2105 | |
2106 | ret = ntfs_fuse_link(old_path, new_path); |
2107 | if (ret) |
2108 | goto restore; |
2109 | |
2110 | ret = ntfs_fuse_unlink(old_path); |
2111 | if (ret) { |
2112 | if (ntfs_fuse_unlink(new_path)) |
2113 | goto err; |
2114 | goto restore; |
2115 | } |
2116 | } |
2117 | |
2118 | goto cleanup; |
2119 | restore: |
2120 | if (ntfs_fuse_link(tmp, new_path)) { |
2121 | err: |
2122 | ntfs_log_perror("Rename failed. Existing file '%s' was renamed " |
2123 | "to '%s'", new_path, tmp); |
2124 | } else { |
2125 | cleanup: |
2126 | /* |
2127 | * Condition for this unlink has already been checked in |
2128 | * "ntfs_fuse_rename_existing_dest()", so it should never |
2129 | * fail (unless concurrent access to directories when fuse |
2130 | * is multithreaded) |
2131 | */ |
2132 | if (ntfs_fuse_unlink(tmp) < 0) |
2133 | ntfs_log_perror("Rename failed. Existing file '%s' still present " |
2134 | "as '%s'", new_path, tmp); |
2135 | } |
2136 | return ret; |
2137 | } |
2138 | |
2139 | static int ntfs_fuse_rename_existing_dest(const char *old_path, const char *new_path) |
2140 | { |
2141 | int ret, len; |
2142 | char *tmp; |
2143 | const char *ext = ".ntfs-3g-"; |
2144 | #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) |
2145 | struct SECURITY_CONTEXT security; |
2146 | #endif |
2147 | |
2148 | ntfs_log_trace("Entering\n"); |
2149 | |
2150 | len = strlen(new_path) + strlen(ext) + 10 + 1; /* wc(str(2^32)) + \0 */ |
2151 | tmp = ntfs_malloc(len); |
2152 | if (!tmp) |
2153 | return -errno; |
2154 | |
2155 | ret = snprintf(tmp, len, "%s%s%010d", new_path, ext, ++ntfs_sequence); |
2156 | if (ret != len - 1) { |
2157 | ntfs_log_error("snprintf failed: %d != %d\n", ret, len - 1); |
2158 | ret = -EOVERFLOW; |
2159 | } else { |
2160 | #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) |
2161 | /* |
2162 | * Make sure existing dest can be removed. |
2163 | * This is only needed if parent directory is |
2164 | * sticky, because in this situation condition |
2165 | * for unlinking is different from condition for |
2166 | * linking |
2167 | */ |
2168 | if (!ntfs_fuse_fill_security_context(&security) |
2169 | || ntfs_allowed_dir_access(&security, new_path, |
2170 | (ntfs_inode*)NULL, (ntfs_inode*)NULL, |
2171 | S_IEXEC + S_IWRITE + S_ISVTX)) |
2172 | ret = ntfs_fuse_safe_rename(old_path, new_path, tmp); |
2173 | else |
2174 | ret = -EACCES; |
2175 | #else |
2176 | ret = ntfs_fuse_safe_rename(old_path, new_path, tmp); |
2177 | #endif |
2178 | } |
2179 | free(tmp); |
2180 | return ret; |
2181 | } |
2182 | |
2183 | static int ntfs_fuse_rename(const char *old_path, const char *new_path) |
2184 | { |
2185 | int ret, stream_name_len; |
2186 | char *path = NULL; |
2187 | ntfschar *stream_name; |
2188 | ntfs_inode *ni; |
2189 | u64 inum; |
2190 | BOOL same; |
2191 | |
2192 | ntfs_log_debug("rename: old: '%s' new: '%s'\n", old_path, new_path); |
2193 | |
2194 | /* |
2195 | * FIXME: Rename should be atomic. |
2196 | */ |
2197 | stream_name_len = ntfs_fuse_parse_path(new_path, &path, &stream_name); |
2198 | if (stream_name_len < 0) |
2199 | return stream_name_len; |
2200 | |
2201 | ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); |
2202 | if (ni) { |
2203 | ret = ntfs_check_empty_dir(ni); |
2204 | if (ret < 0) { |
2205 | ret = -errno; |
2206 | ntfs_inode_close(ni); |
2207 | goto out; |
2208 | } |
2209 | |
2210 | inum = ni->mft_no; |
2211 | if (ntfs_inode_close(ni)) { |
2212 | set_fuse_error(&ret); |
2213 | goto out; |
2214 | } |
2215 | |
2216 | free(path); |
2217 | path = (char*)NULL; |
2218 | if (stream_name_len) |
2219 | free(stream_name); |
2220 | |
2221 | /* silently ignore a rename to same inode */ |
2222 | stream_name_len = ntfs_fuse_parse_path(old_path, |
2223 | &path, &stream_name); |
2224 | if (stream_name_len < 0) |
2225 | return stream_name_len; |
2226 | |
2227 | ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); |
2228 | if (ni) { |
2229 | same = ni->mft_no == inum; |
2230 | if (ntfs_inode_close(ni)) |
2231 | ret = -errno; |
2232 | else |
2233 | if (!same) |
2234 | ret = ntfs_fuse_rename_existing_dest( |
2235 | old_path, new_path); |
2236 | } else |
2237 | ret = -errno; |
2238 | goto out; |
2239 | } |
2240 | |
2241 | ret = ntfs_fuse_link(old_path, new_path); |
2242 | if (ret) |
2243 | goto out; |
2244 | |
2245 | ret = ntfs_fuse_unlink(old_path); |
2246 | if (ret) |
2247 | ntfs_fuse_unlink(new_path); |
2248 | out: |
2249 | free(path); |
2250 | if (stream_name_len) |
2251 | free(stream_name); |
2252 | return ret; |
2253 | } |
2254 | |
2255 | static int ntfs_fuse_mkdir(const char *path, |
2256 | mode_t mode) |
2257 | { |
2258 | if (ntfs_fuse_is_named_data_stream(path)) |
2259 | return -EINVAL; /* n/a for named data streams. */ |
2260 | return ntfs_fuse_create(path, S_IFDIR | (mode & 07777), 0, NULL, |
2261 | (struct fuse_file_info*)NULL); |
2262 | } |
2263 | |
2264 | static int ntfs_fuse_rmdir(const char *path) |
2265 | { |
2266 | if (ntfs_fuse_is_named_data_stream(path)) |
2267 | return -EINVAL; /* n/a for named data streams. */ |
2268 | return ntfs_fuse_rm(path); |
2269 | } |
2270 | |
2271 | #ifdef HAVE_UTIMENSAT |
2272 | |
2273 | static int ntfs_fuse_utimens(const char *path, const struct timespec tv[2]) |
2274 | { |
2275 | ntfs_inode *ni; |
2276 | int res = 0; |
2277 | #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) |
2278 | struct SECURITY_CONTEXT security; |
2279 | #endif |
2280 | |
2281 | if (ntfs_fuse_is_named_data_stream(path)) |
2282 | return -EINVAL; /* n/a for named data streams. */ |
2283 | #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) |
2284 | /* parent directory must be executable */ |
2285 | if (ntfs_fuse_fill_security_context(&security) |
2286 | && !ntfs_allowed_dir_access(&security,path, |
2287 | (ntfs_inode*)NULL,(ntfs_inode*)NULL,S_IEXEC)) { |
2288 | return (-errno); |
2289 | } |
2290 | #endif |
2291 | ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); |
2292 | if (!ni) |
2293 | return -errno; |
2294 | |
2295 | /* no check or update if both UTIME_OMIT */ |
2296 | if ((tv[0].tv_nsec != UTIME_OMIT) || (tv[1].tv_nsec != UTIME_OMIT)) { |
2297 | #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) |
2298 | if (ntfs_allowed_as_owner(&security, ni) |
2299 | || ((tv[0].tv_nsec == UTIME_NOW) |
2300 | && (tv[0].tv_nsec == UTIME_NOW) |
2301 | && ntfs_allowed_access(&security, ni, S_IWRITE))) { |
2302 | #endif |
2303 | ntfs_time_update_flags mask = NTFS_UPDATE_CTIME; |
2304 | |
2305 | if (tv[0].tv_nsec == UTIME_NOW) |
2306 | mask |= NTFS_UPDATE_ATIME; |
2307 | else |
2308 | if (tv[0].tv_nsec != UTIME_OMIT) |
2309 | ni->last_access_time |
2310 | = timespec2ntfs(tv[0]); |
2311 | if (tv[1].tv_nsec == UTIME_NOW) |
2312 | mask |= NTFS_UPDATE_MTIME; |
2313 | else |
2314 | if (tv[1].tv_nsec != UTIME_OMIT) |
2315 | ni->last_data_change_time |
2316 | = timespec2ntfs(tv[1]); |
2317 | ntfs_inode_update_times(ni, mask); |
2318 | #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) |
2319 | } else |
2320 | res = -errno; |
2321 | #endif |
2322 | } |
2323 | if (ntfs_inode_close(ni)) |
2324 | set_fuse_error(&res); |
2325 | return res; |
2326 | } |
2327 | |
2328 | #else /* HAVE_UTIMENSAT */ |
2329 | |
2330 | static int ntfs_fuse_utime(const char *path, struct utimbuf *buf) |
2331 | { |
2332 | ntfs_inode *ni; |
2333 | int res = 0; |
2334 | struct timespec actime; |
2335 | struct timespec modtime; |
2336 | #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) |
2337 | BOOL ownerok; |
2338 | BOOL writeok; |
2339 | struct SECURITY_CONTEXT security; |
2340 | #endif |
2341 | |
2342 | if (ntfs_fuse_is_named_data_stream(path)) |
2343 | return -EINVAL; /* n/a for named data streams. */ |
2344 | #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) |
2345 | /* parent directory must be executable */ |
2346 | if (ntfs_fuse_fill_security_context(&security) |
2347 | && !ntfs_allowed_dir_access(&security,path, |
2348 | (ntfs_inode*)NULL,(ntfs_inode*)NULL,S_IEXEC)) { |
2349 | return (-errno); |
2350 | } |
2351 | #endif |
2352 | ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); |
2353 | if (!ni) |
2354 | return -errno; |
2355 | |
2356 | #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) |
2357 | ownerok = ntfs_allowed_as_owner(&security, ni); |
2358 | if (buf) { |
2359 | /* |
2360 | * fuse never calls with a NULL buf and we do not |
2361 | * know whether the specific condition can be applied |
2362 | * So we have to accept updating by a non-owner having |
2363 | * write access. |
2364 | */ |
2365 | writeok = !ownerok |
2366 | && (buf->actime == buf->modtime) |
2367 | && ntfs_allowed_access(&security, ni, S_IWRITE); |
2368 | /* Must be owner */ |
2369 | if (!ownerok && !writeok) |
2370 | res = (buf->actime == buf->modtime ? -EACCES : -EPERM); |
2371 | else { |
2372 | actime.tv_sec = buf->actime; |
2373 | actime.tv_nsec = 0; |
2374 | modtime.tv_sec = buf->modtime; |
2375 | modtime.tv_nsec = 0; |
2376 | ni->last_access_time = timespec2ntfs(actime); |
2377 | ni->last_data_change_time = timespec2ntfs(modtime); |
2378 | ntfs_fuse_update_times(ni, NTFS_UPDATE_CTIME); |
2379 | } |
2380 | } else { |
2381 | /* Must be owner or have write access */ |
2382 | writeok = !ownerok |
2383 | && ntfs_allowed_access(&security, ni, S_IWRITE); |
2384 | if (!ownerok && !writeok) |
2385 | res = -EACCES; |
2386 | else |
2387 | ntfs_inode_update_times(ni, NTFS_UPDATE_AMCTIME); |
2388 | } |
2389 | #else |
2390 | if (buf) { |
2391 | actime.tv_sec = buf->actime; |
2392 | actime.tv_nsec = 0; |
2393 | modtime.tv_sec = buf->modtime; |
2394 | modtime.tv_nsec = 0; |
2395 | ni->last_access_time = timespec2ntfs(actime); |
2396 | ni->last_data_change_time = timespec2ntfs(modtime); |
2397 | ntfs_fuse_update_times(ni, NTFS_UPDATE_CTIME); |
2398 | } else |
2399 | ntfs_inode_update_times(ni, NTFS_UPDATE_AMCTIME); |
2400 | #endif |
2401 | |
2402 | if (ntfs_inode_close(ni)) |
2403 | set_fuse_error(&res); |
2404 | return res; |
2405 | } |
2406 | |
2407 | #endif /* HAVE_UTIMENSAT */ |
2408 | |
2409 | static int ntfs_fuse_fsync(const char *path __attribute__((unused)), |
2410 | int type __attribute__((unused)), |
2411 | struct fuse_file_info *fi __attribute__((unused))) |
2412 | { |
2413 | int ret; |
2414 | |
2415 | /* sync the full device */ |
2416 | ret = ntfs_device_sync(ctx->vol->dev); |
2417 | if (ret) |
2418 | ret = -errno; |
2419 | return (ret); |
2420 | } |
2421 | |
2422 | static int ntfs_fuse_bmap(const char *path, size_t blocksize, uint64_t *idx) |
2423 | { |
2424 | ntfs_inode *ni; |
2425 | ntfs_attr *na; |
2426 | LCN lcn; |
2427 | int ret = 0; |
2428 | int cl_per_bl = ctx->vol->cluster_size / blocksize; |
2429 | |
2430 | if (blocksize > ctx->vol->cluster_size) |
2431 | return -EINVAL; |
2432 | |
2433 | if (ntfs_fuse_is_named_data_stream(path)) |
2434 | return -EINVAL; |
2435 | |
2436 | ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); |
2437 | if (!ni) |
2438 | return -errno; |
2439 | |
2440 | na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); |
2441 | if (!na) { |
2442 | ret = -errno; |
2443 | goto close_inode; |
2444 | } |
2445 | |
2446 | if ((na->data_flags & (ATTR_COMPRESSION_MASK | ATTR_IS_ENCRYPTED)) |
2447 | || !NAttrNonResident(na)) { |
2448 | ret = -EINVAL; |
2449 | goto close_attr; |
2450 | } |
2451 | |
2452 | if (ntfs_attr_map_whole_runlist(na)) { |
2453 | ret = -errno; |
2454 | goto close_attr; |
2455 | } |
2456 | |
2457 | lcn = ntfs_rl_vcn_to_lcn(na->rl, *idx / cl_per_bl); |
2458 | *idx = (lcn > 0) ? lcn * cl_per_bl + *idx % cl_per_bl : 0; |
2459 | |
2460 | close_attr: |
2461 | ntfs_attr_close(na); |
2462 | close_inode: |
2463 | if (ntfs_inode_close(ni)) |
2464 | set_fuse_error(&ret); |
2465 | return ret; |
2466 | } |
2467 | |
2468 | #ifdef HAVE_SETXATTR |
2469 | |
2470 | /* |
2471 | * Name space identifications and prefixes |
2472 | */ |
2473 | |
2474 | enum { |
2475 | XATTRNS_NONE, |
2476 | XATTRNS_USER, |
2477 | XATTRNS_SYSTEM, |
2478 | XATTRNS_SECURITY, |
2479 | XATTRNS_TRUSTED, |
2480 | XATTRNS_OPEN |
2481 | } ; |
2482 | |
2483 | /* |
2484 | * Check whether access to internal data as an extended |
2485 | * attribute in system name space is allowed |
2486 | * |
2487 | * Returns pointer to inode if allowed, |
2488 | * NULL and errno set if not allowed |
2489 | */ |
2490 | |
2491 | static ntfs_inode *ntfs_check_access_xattr(struct SECURITY_CONTEXT *security, |
2492 | const char *path, int attr, BOOL setting) |
2493 | { |
2494 | ntfs_inode *ni; |
2495 | BOOL foracl; |
2496 | mode_t acctype; |
2497 | |
2498 | ni = (ntfs_inode*)NULL; |
2499 | if (ntfs_fuse_is_named_data_stream(path)) |
2500 | errno = EINVAL; /* n/a for named data streams. */ |
2501 | else { |
2502 | foracl = (attr == XATTR_POSIX_ACC) |
2503 | || (attr == XATTR_POSIX_DEF); |
2504 | /* |
2505 | * When accessing Posix ACL, return unsupported if ACL |
2506 | * were disabled or no user mapping has been defined. |
2507 | * However no error will be returned to getfacl |
2508 | */ |
2509 | if ((!ntfs_fuse_fill_security_context(security) |
2510 | || (ctx->secure_flags |
2511 | & ((1 << SECURITY_DEFAULT) | (1 << SECURITY_RAW)))) |
2512 | && foracl) { |
2513 | errno = EOPNOTSUPP; |
2514 | } else { |
2515 | /* |
2516 | * parent directory must be executable, and |
2517 | * for setting a DOS name it must be writeable |
2518 | */ |
2519 | if (setting && (attr == XATTR_NTFS_DOS_NAME)) |
2520 | acctype = S_IEXEC | S_IWRITE; |
2521 | else |
2522 | acctype = S_IEXEC; |
2523 | if ((attr == XATTR_NTFS_DOS_NAME) |
2524 | && !strcmp(path,"/")) |
2525 | /* forbid getting/setting names on root */ |
2526 | errno = EPERM; |
2527 | else |
2528 | if (ntfs_allowed_real_dir_access(security, path, |
2529 | (ntfs_inode*)NULL ,acctype)) { |
2530 | ni = ntfs_pathname_to_inode(ctx->vol, |
2531 | NULL, path); |
2532 | } |
2533 | } |
2534 | } |
2535 | return (ni); |
2536 | } |
2537 | |
2538 | /* |
2539 | * Determine the name space of an extended attribute |
2540 | */ |
2541 | |
2542 | static int xattr_namespace(const char *name) |
2543 | { |
2544 | int namespace; |
2545 | |
2546 | if (ctx->streams == NF_STREAMS_INTERFACE_XATTR) { |
2547 | namespace = XATTRNS_NONE; |
2548 | if (!strncmp(name, nf_ns_user_prefix, |
2549 | nf_ns_user_prefix_len) |
2550 | && (strlen(name) != (size_t)nf_ns_user_prefix_len)) |
2551 | namespace = XATTRNS_USER; |
2552 | else if (!strncmp(name, nf_ns_system_prefix, |
2553 | nf_ns_system_prefix_len) |
2554 | && (strlen(name) != (size_t)nf_ns_system_prefix_len)) |
2555 | namespace = XATTRNS_SYSTEM; |
2556 | else if (!strncmp(name, nf_ns_security_prefix, |
2557 | nf_ns_security_prefix_len) |
2558 | && (strlen(name) != (size_t)nf_ns_security_prefix_len)) |
2559 | namespace = XATTRNS_SECURITY; |
2560 | else if (!strncmp(name, nf_ns_trusted_prefix, |
2561 | nf_ns_trusted_prefix_len) |
2562 | && (strlen(name) != (size_t)nf_ns_trusted_prefix_len)) |
2563 | namespace = XATTRNS_TRUSTED; |
2564 | } else |
2565 | namespace = XATTRNS_OPEN; |
2566 | return (namespace); |
2567 | } |
2568 | |
2569 | /* |
2570 | * Fix the prefix of an extended attribute |
2571 | */ |
2572 | |
2573 | static int fix_xattr_prefix(const char *name, int namespace, ntfschar **lename) |
2574 | { |
2575 | int len; |
2576 | char *prefixed; |
2577 | |
2578 | *lename = (ntfschar*)NULL; |
2579 | switch (namespace) { |
2580 | case XATTRNS_USER : |
2581 | /* |
2582 | * user name space : remove user prefix |
2583 | */ |
2584 | len = ntfs_mbstoucs(name + nf_ns_user_prefix_len, lename); |
2585 | break; |
2586 | case XATTRNS_SYSTEM : |
2587 | case XATTRNS_SECURITY : |
2588 | case XATTRNS_TRUSTED : |
2589 | /* |
2590 | * security, trusted and unmapped system name spaces : |
2591 | * insert ntfs-3g prefix |
2592 | */ |
2593 | prefixed = ntfs_malloc(strlen(xattr_ntfs_3g) |
2594 | + strlen(name) + 1); |
2595 | if (prefixed) { |
2596 | strcpy(prefixed,xattr_ntfs_3g); |
2597 | strcat(prefixed,name); |
2598 | len = ntfs_mbstoucs(prefixed, lename); |
2599 | free(prefixed); |
2600 | } else |
2601 | len = -1; |
2602 | break; |
2603 | case XATTRNS_OPEN : |
2604 | /* |
2605 | * in open name space mode : do no fix prefix |
2606 | */ |
2607 | len = ntfs_mbstoucs(name, lename); |
2608 | break; |
2609 | default : |
2610 | len = -1; |
2611 | } |
2612 | return (len); |
2613 | } |
2614 | |
2615 | static int ntfs_fuse_listxattr(const char *path, char *list, size_t size) |
2616 | { |
2617 | ntfs_attr_search_ctx *actx = NULL; |
2618 | ntfs_inode *ni; |
2619 | int ret = 0; |
2620 | #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) |
2621 | struct SECURITY_CONTEXT security; |
2622 | #endif |
2623 | #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) |
2624 | /* parent directory must be executable */ |
2625 | if (ntfs_fuse_fill_security_context(&security) |
2626 | && !ntfs_allowed_dir_access(&security,path,(ntfs_inode*)NULL, |
2627 | (ntfs_inode*)NULL,S_IEXEC)) { |
2628 | return (-errno); |
2629 | } |
2630 | #endif |
2631 | ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); |
2632 | if (!ni) |
2633 | return -errno; |
2634 | /* Return with no result for symlinks, fifo, etc. */ |
2635 | if (ni->flags & (FILE_ATTR_SYSTEM | FILE_ATTR_REPARSE_POINT)) |
2636 | goto exit; |
2637 | /* otherwise file must be readable */ |
2638 | #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) |
2639 | if (!ntfs_allowed_access(&security,ni,S_IREAD)) { |
2640 | ret = -EACCES; |
2641 | goto exit; |
2642 | } |
2643 | #endif |
2644 | actx = ntfs_attr_get_search_ctx(ni, NULL); |
2645 | if (!actx) { |
2646 | ret = -errno; |
2647 | goto exit; |
2648 | } |
2649 | |
2650 | if ((ctx->streams == NF_STREAMS_INTERFACE_XATTR) |
2651 | || (ctx->streams == NF_STREAMS_INTERFACE_OPENXATTR)) { |
2652 | ret = ntfs_fuse_listxattr_common(ni, actx, list, size, |
2653 | ctx->streams == NF_STREAMS_INTERFACE_XATTR); |
2654 | if (ret < 0) |
2655 | goto exit; |
2656 | } |
2657 | if (errno != ENOENT) |
2658 | ret = -errno; |
2659 | exit: |
2660 | if (actx) |
2661 | ntfs_attr_put_search_ctx(actx); |
2662 | if (ntfs_inode_close(ni)) |
2663 | set_fuse_error(&ret); |
2664 | return ret; |
2665 | } |
2666 | |
2667 | static int ntfs_fuse_getxattr_windows(const char *path, const char *name, |
2668 | char *value, size_t size) |
2669 | { |
2670 | ntfs_attr_search_ctx *actx = NULL; |
2671 | ntfs_inode *ni; |
2672 | char *to = value; |
2673 | int ret = 0; |
2674 | #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) |
2675 | struct SECURITY_CONTEXT security; |
2676 | #endif |
2677 | |
2678 | if (strcmp(name, "ntfs.streams.list")) |
2679 | return -EOPNOTSUPP; |
2680 | #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) |
2681 | /* parent directory must be executable */ |
2682 | if (ntfs_fuse_fill_security_context(&security) |
2683 | && !ntfs_allowed_dir_access(&security,path,(ntfs_inode*)NULL, |
2684 | (ntfs_inode*)NULL,S_IEXEC)) { |
2685 | return (-errno); |
2686 | } |
2687 | #endif |
2688 | ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); |
2689 | if (!ni) |
2690 | return -errno; |
2691 | #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) |
2692 | if (!ntfs_allowed_access(&security,ni,S_IREAD)) { |
2693 | ret = -errno; |
2694 | goto exit; |
2695 | } |
2696 | #endif |
2697 | actx = ntfs_attr_get_search_ctx(ni, NULL); |
2698 | if (!actx) { |
2699 | ret = -errno; |
2700 | goto exit; |
2701 | } |
2702 | while (!ntfs_attr_lookup(AT_DATA, NULL, 0, CASE_SENSITIVE, |
2703 | 0, NULL, 0, actx)) { |
2704 | char *tmp_name = NULL; |
2705 | int tmp_name_len; |
2706 | |
2707 | if (!actx->attr->name_length) |
2708 | continue; |
2709 | tmp_name_len = ntfs_ucstombs((ntfschar *)((u8*)actx->attr + |
2710 | le16_to_cpu(actx->attr->name_offset)), |
2711 | actx->attr->name_length, &tmp_name, 0); |
2712 | if (tmp_name_len < 0) { |
2713 | ret = -errno; |
2714 | goto exit; |
2715 | } |
2716 | if (ret) |
2717 | ret++; /* For space delimiter. */ |
2718 | ret += tmp_name_len; |
2719 | if (size) { |
2720 | if ((size_t)ret <= size) { |
2721 | /* Don't add space to the beginning of line. */ |
2722 | if (to != value) { |
2723 | *to = '\0'; |
2724 | to++; |
2725 | } |
2726 | strncpy(to, tmp_name, tmp_name_len); |
2727 | to += tmp_name_len; |
2728 | } else { |
2729 | free(tmp_name); |
2730 | ret = -ERANGE; |
2731 | goto exit; |
2732 | } |
2733 | } |
2734 | free(tmp_name); |
2735 | } |
2736 | if (errno != ENOENT) |
2737 | ret = -errno; |
2738 | exit: |
2739 | if (actx) |
2740 | ntfs_attr_put_search_ctx(actx); |
2741 | if (ntfs_inode_close(ni)) |
2742 | set_fuse_error(&ret); |
2743 | return ret; |
2744 | } |
2745 | |
2746 | static int ntfs_fuse_getxattr(const char *path, const char *name, |
2747 | char *value, size_t size) |
2748 | { |
2749 | ntfs_inode *ni; |
2750 | ntfs_inode *dir_ni; |
2751 | ntfs_attr *na = NULL; |
2752 | ntfschar *lename = NULL; |
2753 | int res, lename_len; |
2754 | s64 rsize; |
2755 | enum SYSTEMXATTRS attr; |
2756 | int namespace; |
2757 | struct SECURITY_CONTEXT security; |
2758 | |
2759 | attr = ntfs_xattr_system_type(name,ctx->vol); |
2760 | if (attr != XATTR_UNMAPPED) { |
2761 | #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) |
2762 | /* |
2763 | * hijack internal data and ACL retrieval, whatever |
2764 | * mode was selected for xattr (from the user's |
2765 | * point of view, ACLs are not xattr) |
2766 | */ |
2767 | ni = ntfs_check_access_xattr(&security, path, attr, FALSE); |
2768 | if (ni) { |
2769 | if (ntfs_allowed_access(&security,ni,S_IREAD)) { |
2770 | if (attr == XATTR_NTFS_DOS_NAME) |
2771 | dir_ni = get_parent_dir(path); |
2772 | else |
2773 | dir_ni = (ntfs_inode*)NULL; |
2774 | res = ntfs_xattr_system_getxattr(&security, |
2775 | attr, ni, dir_ni, value, size); |
2776 | if (dir_ni && ntfs_inode_close(dir_ni)) |
2777 | set_fuse_error(&res); |
2778 | } else { |
2779 | res = -errno; |
2780 | } |
2781 | if (ntfs_inode_close(ni)) |
2782 | set_fuse_error(&res); |
2783 | } else |
2784 | res = -errno; |
2785 | #else |
2786 | /* |
2787 | * Only hijack NTFS ACL retrieval if POSIX ACLS |
2788 | * option is not selected |
2789 | * Access control is done by fuse |
2790 | */ |
2791 | if (ntfs_fuse_is_named_data_stream(path)) |
2792 | res = -EINVAL; /* n/a for named data streams. */ |
2793 | else { |
2794 | ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); |
2795 | if (ni) { |
2796 | /* user mapping not mandatory */ |
2797 | ntfs_fuse_fill_security_context(&security); |
2798 | if (attr == XATTR_NTFS_DOS_NAME) |
2799 | dir_ni = get_parent_dir(path); |
2800 | else |
2801 | dir_ni = (ntfs_inode*)NULL; |
2802 | res = ntfs_xattr_system_getxattr(&security, |
2803 | attr, ni, dir_ni, value, size); |
2804 | if (dir_ni && ntfs_inode_close(dir_ni)) |
2805 | set_fuse_error(&res); |
2806 | if (ntfs_inode_close(ni)) |
2807 | set_fuse_error(&res); |
2808 | } else |
2809 | res = -errno; |
2810 | } |
2811 | #endif |
2812 | return (res); |
2813 | } |
2814 | if (ctx->streams == NF_STREAMS_INTERFACE_WINDOWS) |
2815 | return ntfs_fuse_getxattr_windows(path, name, value, size); |
2816 | if (ctx->streams == NF_STREAMS_INTERFACE_NONE) |
2817 | return -EOPNOTSUPP; |
2818 | namespace = xattr_namespace(name); |
2819 | if (namespace == XATTRNS_NONE) |
2820 | return -EOPNOTSUPP; |
2821 | #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) |
2822 | /* parent directory must be executable */ |
2823 | if (ntfs_fuse_fill_security_context(&security) |
2824 | && !ntfs_allowed_dir_access(&security,path,(ntfs_inode*)NULL, |
2825 | (ntfs_inode*)NULL,S_IEXEC)) { |
2826 | return (-errno); |
2827 | } |
2828 | /* trusted only readable by root */ |
2829 | if ((namespace == XATTRNS_TRUSTED) |
2830 | && security.uid) |
2831 | return -ENODATA; |
2832 | #endif |
2833 | ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); |
2834 | if (!ni) |
2835 | return -errno; |
2836 | /* Return with no result for symlinks, fifo, etc. */ |
2837 | if (ni->flags & (FILE_ATTR_SYSTEM | FILE_ATTR_REPARSE_POINT)) { |
2838 | res = -ENODATA; |
2839 | goto exit; |
2840 | } |
2841 | /* otherwise file must be readable */ |
2842 | #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) |
2843 | if (!ntfs_allowed_access(&security, ni, S_IREAD)) { |
2844 | res = -errno; |
2845 | goto exit; |
2846 | } |
2847 | #endif |
2848 | lename_len = fix_xattr_prefix(name, namespace, &lename); |
2849 | if (lename_len == -1) { |
2850 | res = -errno; |
2851 | goto exit; |
2852 | } |
2853 | na = ntfs_attr_open(ni, AT_DATA, lename, lename_len); |
2854 | if (!na) { |
2855 | res = -ENODATA; |
2856 | goto exit; |
2857 | } |
2858 | rsize = na->data_size; |
2859 | if (ctx->efs_raw |
2860 | && rsize |
2861 | && (na->data_flags & ATTR_IS_ENCRYPTED) |
2862 | && NAttrNonResident(na)) |
2863 | rsize = ((na->data_size + 511) & ~511) + 2; |
2864 | if (size) { |
2865 | if (size >= (size_t)rsize) { |
2866 | res = ntfs_attr_pread(na, 0, rsize, value); |
2867 | if (res != rsize) |
2868 | res = -errno; |
2869 | } else |
2870 | res = -ERANGE; |
2871 | } else |
2872 | res = rsize; |
2873 | exit: |
2874 | if (na) |
2875 | ntfs_attr_close(na); |
2876 | free(lename); |
2877 | if (ntfs_inode_close(ni)) |
2878 | set_fuse_error(&res); |
2879 | return res; |
2880 | } |
2881 | |
2882 | static int ntfs_fuse_setxattr(const char *path, const char *name, |
2883 | const char *value, size_t size, int flags) |
2884 | { |
2885 | ntfs_inode *ni; |
2886 | ntfs_inode *dir_ni; |
2887 | ntfs_attr *na = NULL; |
2888 | ntfschar *lename = NULL; |
2889 | int res, lename_len; |
2890 | size_t total; |
2891 | s64 part; |
2892 | enum SYSTEMXATTRS attr; |
2893 | int namespace; |
2894 | struct SECURITY_CONTEXT security; |
2895 | |
2896 | attr = ntfs_xattr_system_type(name,ctx->vol); |
2897 | if (attr != XATTR_UNMAPPED) { |
2898 | #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) |
2899 | /* |
2900 | * hijack internal data and ACL setting, whatever |
2901 | * mode was selected for xattr (from the user's |
2902 | * point of view, ACLs are not xattr) |
2903 | * Note : updating an ACL does not set ctime |
2904 | */ |
2905 | ni = ntfs_check_access_xattr(&security,path,attr,TRUE); |
2906 | if (ni) { |
2907 | if (ntfs_allowed_as_owner(&security,ni)) { |
2908 | if (attr == XATTR_NTFS_DOS_NAME) |
2909 | dir_ni = get_parent_dir(path); |
2910 | else |
2911 | dir_ni = (ntfs_inode*)NULL; |
2912 | res = ntfs_xattr_system_setxattr(&security, |
2913 | attr, ni, dir_ni, value, size, flags); |
2914 | /* never have to close dir_ni */ |
2915 | if (res) |
2916 | res = -errno; |
2917 | } else |
2918 | res = -errno; |
2919 | if ((attr != XATTR_NTFS_DOS_NAME) |
2920 | && ntfs_inode_close(ni)) |
2921 | set_fuse_error(&res); |
2922 | } else |
2923 | res = -errno; |
2924 | #else |
2925 | /* |
2926 | * Only hijack NTFS ACL setting if POSIX ACLS |
2927 | * option is not selected |
2928 | * Access control is partially done by fuse |
2929 | */ |
2930 | if (ntfs_fuse_is_named_data_stream(path)) |
2931 | res = -EINVAL; /* n/a for named data streams. */ |
2932 | else { |
2933 | /* creation of a new name is not controlled by fuse */ |
2934 | if (attr == XATTR_NTFS_DOS_NAME) |
2935 | ni = ntfs_check_access_xattr(&security,path,attr,TRUE); |
2936 | else |
2937 | ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); |
2938 | if (ni) { |
2939 | /* |
2940 | * user mapping is not mandatory |
2941 | * if defined, only owner is allowed |
2942 | */ |
2943 | if (!ntfs_fuse_fill_security_context(&security) |
2944 | || ntfs_allowed_as_owner(&security,ni)) { |
2945 | if (attr == XATTR_NTFS_DOS_NAME) |
2946 | dir_ni = get_parent_dir(path); |
2947 | else |
2948 | dir_ni = (ntfs_inode*)NULL; |
2949 | res = ntfs_xattr_system_setxattr(&security, |
2950 | attr, ni, dir_ni, value, |
2951 | size, flags); |
2952 | /* never have to close dir_ni */ |
2953 | if (res) |
2954 | res = -errno; |
2955 | } else |
2956 | res = -errno; |
2957 | if ((attr != XATTR_NTFS_DOS_NAME) |
2958 | && ntfs_inode_close(ni)) |
2959 | set_fuse_error(&res); |
2960 | } else |
2961 | res = -errno; |
2962 | } |
2963 | #endif |
2964 | return (res); |
2965 | } |
2966 | if ((ctx->streams != NF_STREAMS_INTERFACE_XATTR) |
2967 | && (ctx->streams != NF_STREAMS_INTERFACE_OPENXATTR)) |
2968 | return -EOPNOTSUPP; |
2969 | namespace = xattr_namespace(name); |
2970 | if (namespace == XATTRNS_NONE) |
2971 | return -EOPNOTSUPP; |
2972 | #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) |
2973 | /* parent directory must be executable */ |
2974 | if (ntfs_fuse_fill_security_context(&security) |
2975 | && !ntfs_allowed_dir_access(&security,path,(ntfs_inode*)NULL, |
2976 | (ntfs_inode*)NULL,S_IEXEC)) { |
2977 | return (-errno); |
2978 | } |
2979 | /* security and trusted only settable by root */ |
2980 | if (((namespace == XATTRNS_SECURITY) |
2981 | || (namespace == XATTRNS_TRUSTED)) |
2982 | && security.uid) |
2983 | return -EPERM; |
2984 | #endif |
2985 | ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); |
2986 | if (!ni) |
2987 | return -errno; |
2988 | #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) |
2989 | switch (namespace) { |
2990 | case XATTRNS_SECURITY : |
2991 | case XATTRNS_TRUSTED : |
2992 | if (security.uid) { |
2993 | res = -EPERM; |
2994 | goto exit; |
2995 | } |
2996 | break; |
2997 | case XATTRNS_SYSTEM : |
2998 | if (!ntfs_allowed_as_owner(&security,ni)) { |
2999 | res = -EACCES; |
3000 | goto exit; |
3001 | } |
3002 | break; |
3003 | default : |
3004 | /* User xattr not allowed for symlinks, fifo, etc. */ |
3005 | if (ni->flags & (FILE_ATTR_SYSTEM | FILE_ATTR_REPARSE_POINT)) { |
3006 | res = -EPERM; |
3007 | goto exit; |
3008 | } |
3009 | if (!ntfs_allowed_access(&security,ni,S_IWRITE)) { |
3010 | res = -EACCES; |
3011 | goto exit; |
3012 | } |
3013 | break; |
3014 | } |
3015 | #else |
3016 | /* User xattr not allowed for symlinks, fifo, etc. */ |
3017 | if ((namespace == XATTRNS_USER) |
3018 | && (ni->flags & (FILE_ATTR_SYSTEM | FILE_ATTR_REPARSE_POINT))) { |
3019 | res = -EPERM; |
3020 | goto exit; |
3021 | } |
3022 | #endif |
3023 | lename_len = fix_xattr_prefix(name, namespace, &lename); |
3024 | if ((lename_len == -1) |
3025 | || (ctx->windows_names |
3026 | && ntfs_forbidden_chars(lename,lename_len))) { |
3027 | res = -errno; |
3028 | goto exit; |
3029 | } |
3030 | na = ntfs_attr_open(ni, AT_DATA, lename, lename_len); |
3031 | if (na && flags == XATTR_CREATE) { |
3032 | res = -EEXIST; |
3033 | goto exit; |
3034 | } |
3035 | if (!na) { |
3036 | if (flags == XATTR_REPLACE) { |
3037 | res = -ENODATA; |
3038 | goto exit; |
3039 | } |
3040 | if (ntfs_attr_add(ni, AT_DATA, lename, lename_len, NULL, 0)) { |
3041 | res = -errno; |
3042 | goto exit; |
3043 | } |
3044 | if (!(ni->flags & FILE_ATTR_ARCHIVE)) { |
3045 | set_archive(ni); |
3046 | NInoFileNameSetDirty(ni); |
3047 | } |
3048 | na = ntfs_attr_open(ni, AT_DATA, lename, lename_len); |
3049 | if (!na) { |
3050 | res = -errno; |
3051 | goto exit; |
3052 | } |
3053 | } else { |
3054 | /* currently compressed streams can only be wiped out */ |
3055 | if (ntfs_attr_truncate(na, (s64)0 /* size */)) { |
3056 | res = -errno; |
3057 | goto exit; |
3058 | } |
3059 | } |
3060 | total = 0; |
3061 | res = 0; |
3062 | if (size) { |
3063 | do { |
3064 | part = ntfs_attr_pwrite(na, total, size - total, |
3065 | &value[total]); |
3066 | if (part > 0) |
3067 | total += part; |
3068 | } while ((part > 0) && (total < size)); |
3069 | } |
3070 | if ((total != size) || ntfs_attr_pclose(na)) |
3071 | res = -errno; |
3072 | else { |
3073 | if (ctx->efs_raw |
3074 | && (ni->flags & FILE_ATTR_ENCRYPTED)) { |
3075 | if (ntfs_efs_fixup_attribute(NULL,na)) |
3076 | res = -errno; |
3077 | } |
3078 | } |
3079 | if (!res && !(ni->flags & FILE_ATTR_ARCHIVE)) { |
3080 | set_archive(ni); |
3081 | NInoFileNameSetDirty(ni); |
3082 | } |
3083 | exit: |
3084 | if (na) |
3085 | ntfs_attr_close(na); |
3086 | free(lename); |
3087 | if (ntfs_inode_close(ni)) |
3088 | set_fuse_error(&res); |
3089 | return res; |
3090 | } |
3091 | |
3092 | static int ntfs_fuse_removexattr(const char *path, const char *name) |
3093 | { |
3094 | ntfs_inode *ni; |
3095 | ntfs_inode *dir_ni; |
3096 | ntfschar *lename = NULL; |
3097 | int res = 0, lename_len; |
3098 | enum SYSTEMXATTRS attr; |
3099 | int namespace; |
3100 | struct SECURITY_CONTEXT security; |
3101 | |
3102 | attr = ntfs_xattr_system_type(name,ctx->vol); |
3103 | if (attr != XATTR_UNMAPPED) { |
3104 | switch (attr) { |
3105 | /* |
3106 | * Removal of NTFS ACL, ATTRIB, EFSINFO or TIMES |
3107 | * is never allowed |
3108 | */ |
3109 | case XATTR_NTFS_ACL : |
3110 | case XATTR_NTFS_ATTRIB : |
3111 | case XATTR_NTFS_ATTRIB_BE : |
3112 | case XATTR_NTFS_EFSINFO : |
3113 | case XATTR_NTFS_TIMES : |
3114 | case XATTR_NTFS_TIMES_BE : |
3115 | case XATTR_NTFS_CRTIME : |
3116 | case XATTR_NTFS_CRTIME_BE : |
3117 | res = -EPERM; |
3118 | break; |
3119 | default : |
3120 | #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) |
3121 | /* |
3122 | * hijack internal data and ACL removal, whatever |
3123 | * mode was selected for xattr (from the user's |
3124 | * point of view, ACLs are not xattr) |
3125 | * Note : updating an ACL does not set ctime |
3126 | */ |
3127 | ni = ntfs_check_access_xattr(&security,path,attr,TRUE); |
3128 | if (ni) { |
3129 | if (ntfs_allowed_as_owner(&security,ni)) { |
3130 | if (attr == XATTR_NTFS_DOS_NAME) |
3131 | dir_ni = get_parent_dir(path); |
3132 | else |
3133 | dir_ni = (ntfs_inode*)NULL; |
3134 | res = ntfs_xattr_system_removexattr(&security, |
3135 | attr, ni, dir_ni); |
3136 | /* never have to close dir_ni */ |
3137 | if (res) |
3138 | res = -errno; |
3139 | } else |
3140 | res = -errno; |
3141 | if ((attr != XATTR_NTFS_DOS_NAME) |
3142 | && ntfs_inode_close(ni)) |
3143 | set_fuse_error(&res); |
3144 | } else |
3145 | res = -errno; |
3146 | #else |
3147 | /* |
3148 | * Only hijack NTFS ACL setting if POSIX ACLS |
3149 | * option is not selected |
3150 | * Access control is partially done by fuse |
3151 | */ |
3152 | /* creation of a new name is not controlled by fuse */ |
3153 | if (attr == XATTR_NTFS_DOS_NAME) |
3154 | ni = ntfs_check_access_xattr(&security, |
3155 | path, attr, TRUE); |
3156 | else { |
3157 | if (ntfs_fuse_is_named_data_stream(path)) { |
3158 | ni = (ntfs_inode*)NULL; |
3159 | errno = EINVAL; /* n/a for named data streams. */ |
3160 | } else |
3161 | ni = ntfs_pathname_to_inode(ctx->vol, |
3162 | NULL, path); |
3163 | } |
3164 | if (ni) { |
3165 | /* |
3166 | * user mapping is not mandatory |
3167 | * if defined, only owner is allowed |
3168 | */ |
3169 | if (!ntfs_fuse_fill_security_context(&security) |
3170 | || ntfs_allowed_as_owner(&security,ni)) { |
3171 | if (attr == XATTR_NTFS_DOS_NAME) |
3172 | dir_ni = get_parent_dir(path); |
3173 | else |
3174 | dir_ni = (ntfs_inode*)NULL; |
3175 | res = ntfs_xattr_system_removexattr(&security, |
3176 | attr, ni, dir_ni); |
3177 | /* never have to close dir_ni */ |
3178 | if (res) |
3179 | res = -errno; |
3180 | } else |
3181 | res = -errno; |
3182 | if ((attr != XATTR_NTFS_DOS_NAME) |
3183 | && ntfs_inode_close(ni)) |
3184 | set_fuse_error(&res); |
3185 | } else |
3186 | res = -errno; |
3187 | #endif |
3188 | break; |
3189 | } |
3190 | return (res); |
3191 | } |
3192 | if ((ctx->streams != NF_STREAMS_INTERFACE_XATTR) |
3193 | && (ctx->streams != NF_STREAMS_INTERFACE_OPENXATTR)) |
3194 | return -EOPNOTSUPP; |
3195 | namespace = xattr_namespace(name); |
3196 | if (namespace == XATTRNS_NONE) |
3197 | return -EOPNOTSUPP; |
3198 | #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) |
3199 | /* parent directory must be executable */ |
3200 | if (ntfs_fuse_fill_security_context(&security) |
3201 | && !ntfs_allowed_dir_access(&security,path,(ntfs_inode*)NULL, |
3202 | (ntfs_inode*)NULL,S_IEXEC)) { |
3203 | return (-errno); |
3204 | } |
3205 | /* security and trusted only settable by root */ |
3206 | if (((namespace == XATTRNS_SECURITY) |
3207 | || (namespace == XATTRNS_TRUSTED)) |
3208 | && security.uid) |
3209 | return -EACCES; |
3210 | #endif |
3211 | ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); |
3212 | if (!ni) |
3213 | return -errno; |
3214 | #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) |
3215 | switch (namespace) { |
3216 | case XATTRNS_SECURITY : |
3217 | case XATTRNS_TRUSTED : |
3218 | if (security.uid) { |
3219 | res = -EPERM; |
3220 | goto exit; |
3221 | } |
3222 | break; |
3223 | case XATTRNS_SYSTEM : |
3224 | if (!ntfs_allowed_as_owner(&security,ni)) { |
3225 | res = -EACCES; |
3226 | goto exit; |
3227 | } |
3228 | break; |
3229 | default : |
3230 | /* User xattr not allowed for symlinks, fifo, etc. */ |
3231 | if (ni->flags & (FILE_ATTR_SYSTEM | FILE_ATTR_REPARSE_POINT)) { |
3232 | res = -EPERM; |
3233 | goto exit; |
3234 | } |
3235 | if (!ntfs_allowed_access(&security,ni,S_IWRITE)) { |
3236 | res = -EACCES; |
3237 | goto exit; |
3238 | } |
3239 | break; |
3240 | } |
3241 | #else |
3242 | /* User xattr not allowed for symlinks, fifo, etc. */ |
3243 | if ((namespace == XATTRNS_USER) |
3244 | && (ni->flags & (FILE_ATTR_SYSTEM | FILE_ATTR_REPARSE_POINT))) { |
3245 | res = -EPERM; |
3246 | goto exit; |
3247 | } |
3248 | #endif |
3249 | lename_len = fix_xattr_prefix(name, namespace, &lename); |
3250 | if (lename_len == -1) { |
3251 | res = -errno; |
3252 | goto exit; |
3253 | } |
3254 | if (ntfs_attr_remove(ni, AT_DATA, lename, lename_len)) { |
3255 | if (errno == ENOENT) |
3256 | errno = ENODATA; |
3257 | res = -errno; |
3258 | } |
3259 | if (!(ni->flags & FILE_ATTR_ARCHIVE)) { |
3260 | set_archive(ni); |
3261 | NInoFileNameSetDirty(ni); |
3262 | } |
3263 | exit: |
3264 | free(lename); |
3265 | if (ntfs_inode_close(ni)) |
3266 | set_fuse_error(&res); |
3267 | return res; |
3268 | } |
3269 | |
3270 | #else |
3271 | #if POSIXACLS |
3272 | #error "Option inconsistency : POSIXACLS requires SETXATTR" |
3273 | #endif |
3274 | #endif /* HAVE_SETXATTR */ |
3275 | |
3276 | static void ntfs_close(void) |
3277 | { |
3278 | struct SECURITY_CONTEXT security; |
3279 | |
3280 | if (!ctx) |
3281 | return; |
3282 | |
3283 | if (!ctx->vol) |
3284 | return; |
3285 | |
3286 | if (ctx->mounted) { |
3287 | ntfs_log_info("Unmounting %s (%s)\n", opts.device, |
3288 | ctx->vol->vol_name); |
3289 | if (ntfs_fuse_fill_security_context(&security)) { |
3290 | if (ctx->seccache && ctx->seccache->head.p_reads) { |
3291 | ntfs_log_info("Permissions cache : %lu writes, " |
3292 | "%lu reads, %lu.%1lu%% hits\n", |
3293 | ctx->seccache->head.p_writes, |
3294 | ctx->seccache->head.p_reads, |
3295 | 100 * ctx->seccache->head.p_hits |
3296 | / ctx->seccache->head.p_reads, |
3297 | 1000 * ctx->seccache->head.p_hits |
3298 | / ctx->seccache->head.p_reads % 10); |
3299 | } |
3300 | } |
3301 | ntfs_close_secure(&security); |
3302 | } |
3303 | |
3304 | if (ntfs_umount(ctx->vol, FALSE)) |
3305 | ntfs_log_perror("Failed to close volume %s", opts.device); |
3306 | |
3307 | ctx->vol = NULL; |
3308 | } |
3309 | |
3310 | static void ntfs_fuse_destroy2(void *unused __attribute__((unused))) |
3311 | { |
3312 | ntfs_close(); |
3313 | } |
3314 | |
3315 | static struct fuse_operations ntfs_3g_ops = { |
3316 | .getattr = ntfs_fuse_getattr, |
3317 | .readlink = ntfs_fuse_readlink, |
3318 | .readdir = ntfs_fuse_readdir, |
3319 | .open = ntfs_fuse_open, |
3320 | .release = ntfs_fuse_release, |
3321 | .read = ntfs_fuse_read, |
3322 | .write = ntfs_fuse_write, |
3323 | .truncate = ntfs_fuse_truncate, |
3324 | .ftruncate = ntfs_fuse_ftruncate, |
3325 | #if HAVE_SYS_STATVFS_H |
3326 | .statfs = ntfs_fuse_statfs, |
3327 | #endif |
3328 | .chmod = ntfs_fuse_chmod, |
3329 | .chown = ntfs_fuse_chown, |
3330 | .create = ntfs_fuse_create_file, |
3331 | .mknod = ntfs_fuse_mknod, |
3332 | .symlink = ntfs_fuse_symlink, |
3333 | .link = ntfs_fuse_link, |
3334 | .unlink = ntfs_fuse_unlink, |
3335 | .rename = ntfs_fuse_rename, |
3336 | .mkdir = ntfs_fuse_mkdir, |
3337 | .rmdir = ntfs_fuse_rmdir, |
3338 | #ifdef HAVE_UTIMENSAT |
3339 | .utimens = ntfs_fuse_utimens, |
3340 | #else |
3341 | .utime = ntfs_fuse_utime, |
3342 | #endif |
3343 | .fsync = ntfs_fuse_fsync, |
3344 | .fsyncdir = ntfs_fuse_fsync, |
3345 | .bmap = ntfs_fuse_bmap, |
3346 | .destroy = ntfs_fuse_destroy2, |
3347 | #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) |
3348 | .access = ntfs_fuse_access, |
3349 | .opendir = ntfs_fuse_opendir, |
3350 | #endif |
3351 | #ifdef HAVE_SETXATTR |
3352 | .getxattr = ntfs_fuse_getxattr, |
3353 | .setxattr = ntfs_fuse_setxattr, |
3354 | .removexattr = ntfs_fuse_removexattr, |
3355 | .listxattr = ntfs_fuse_listxattr, |
3356 | #endif /* HAVE_SETXATTR */ |
3357 | #if defined(__APPLE__) || defined(__DARWIN__) |
3358 | /* MacFUSE extensions. */ |
3359 | .getxtimes = ntfs_macfuse_getxtimes, |
3360 | .setcrtime = ntfs_macfuse_setcrtime, |
3361 | .setbkuptime = ntfs_macfuse_setbkuptime, |
3362 | .setchgtime = ntfs_macfuse_setchgtime, |
3363 | #endif /* defined(__APPLE__) || defined(__DARWIN__) */ |
3364 | #if defined(FUSE_CAP_DONT_MASK) || defined(FUSE_CAP_BIG_WRITES) \ |
3365 | || (defined(__APPLE__) || defined(__DARWIN__)) |
3366 | .init = ntfs_init |
3367 | #endif |
3368 | }; |
3369 | |
3370 | static int ntfs_fuse_init(void) |
3371 | { |
3372 | ctx = ntfs_calloc(sizeof(ntfs_fuse_context_t)); |
3373 | if (!ctx) |
3374 | return -1; |
3375 | |
3376 | *ctx = (ntfs_fuse_context_t) { |
3377 | .uid = getuid(), |
3378 | .gid = getgid(), |
3379 | #if defined(linux) |
3380 | .streams = NF_STREAMS_INTERFACE_XATTR, |
3381 | #else |
3382 | .streams = NF_STREAMS_INTERFACE_NONE, |
3383 | #endif |
3384 | .atime = ATIME_RELATIVE, |
3385 | .silent = TRUE, |
3386 | .recover = TRUE |
3387 | }; |
3388 | return 0; |
3389 | } |
3390 | |
3391 | static int ntfs_open(const char *device) |
3392 | { |
3393 | unsigned long flags = 0; |
3394 | |
3395 | if (!ctx->blkdev) |
3396 | flags |= NTFS_MNT_EXCLUSIVE; |
3397 | if (ctx->ro) |
3398 | flags |= NTFS_MNT_RDONLY; |
3399 | if (ctx->recover) |
3400 | flags |= NTFS_MNT_RECOVER; |
3401 | if (ctx->hiberfile) |
3402 | flags |= NTFS_MNT_IGNORE_HIBERFILE; |
3403 | |
3404 | ctx->vol = ntfs_mount(device, flags); |
3405 | if (!ctx->vol) { |
3406 | ntfs_log_perror("Failed to mount '%s'", device); |
3407 | goto err_out; |
3408 | } |
3409 | if (ctx->sync && ctx->vol->dev) |
3410 | NDevSetSync(ctx->vol->dev); |
3411 | if (ctx->compression) |
3412 | NVolSetCompression(ctx->vol); |
3413 | else |
3414 | NVolClearCompression(ctx->vol); |
3415 | #ifdef HAVE_SETXATTR |
3416 | /* archivers must see hidden files */ |
3417 | if (ctx->efs_raw) |
3418 | ctx->hide_hid_files = FALSE; |
3419 | #endif |
3420 | if (ntfs_set_shown_files(ctx->vol, ctx->show_sys_files, |
3421 | !ctx->hide_hid_files, ctx->hide_dot_files)) |
3422 | goto err_out; |
3423 | |
3424 | ctx->vol->free_clusters = ntfs_attr_get_free_bits(ctx->vol->lcnbmp_na); |
3425 | if (ctx->vol->free_clusters < 0) { |
3426 | ntfs_log_perror("Failed to read NTFS $Bitmap"); |
3427 | goto err_out; |
3428 | } |
3429 | |
3430 | ctx->vol->free_mft_records = ntfs_get_nr_free_mft_records(ctx->vol); |
3431 | if (ctx->vol->free_mft_records < 0) { |
3432 | ntfs_log_perror("Failed to calculate free MFT records"); |
3433 | goto err_out; |
3434 | } |
3435 | |
3436 | if (ctx->hiberfile && ntfs_volume_check_hiberfile(ctx->vol, 0)) { |
3437 | if (errno != EPERM) |
3438 | goto err_out; |
3439 | if (ntfs_fuse_rm("/hiberfil.sys")) |
3440 | goto err_out; |
3441 | } |
3442 | |
3443 | errno = 0; |
3444 | err_out: |
3445 | return ntfs_volume_error(errno); |
3446 | |
3447 | } |
3448 | |
3449 | static void usage(void) |
3450 | { |
3451 | ntfs_log_info(usage_msg, EXEC_NAME, VERSION, FUSE_TYPE, fuse_version(), |
3452 | 4 + POSIXACLS*6 - KERNELPERMS*3 + CACHEING, |
3453 | EXEC_NAME, ntfs_home); |
3454 | } |
3455 | |
3456 | #if defined(linux) || defined(__uClinux__) |
3457 | |
3458 | static const char *dev_fuse_msg = |
3459 | "HINT: You should be root, or make ntfs-3g setuid root, or load the FUSE\n" |
3460 | " kernel module as root ('modprobe fuse' or 'insmod <path_to>/fuse.ko'" |
3461 | " or insmod <path_to>/fuse.o'). Make also sure that the fuse device" |
3462 | " exists. It's usually either /dev/fuse or /dev/misc/fuse."; |
3463 | |
3464 | static const char *fuse26_kmod_msg = |
3465 | "WARNING: Deficient Linux kernel detected. Some driver features are\n" |
3466 | " not available (swap file on NTFS, boot from NTFS by LILO), and\n" |
3467 | " unmount is not safe unless it's made sure the ntfs-3g process\n" |
3468 | " naturally terminates after calling 'umount'. If you wish this\n" |
3469 | " message to disappear then you should upgrade to at least kernel\n" |
3470 | " version 2.6.20, or request help from your distribution to fix\n" |
3471 | " the kernel problem. The below web page has more information:\n" |
3472 | " http://tuxera.com/community/ntfs-3g-faq/#fuse26\n" |
3473 | "\n"; |
3474 | |
3475 | static void mknod_dev_fuse(const char *dev) |
3476 | { |
3477 | struct stat st; |
3478 | |
3479 | if (stat(dev, &st) && (errno == ENOENT)) { |
3480 | mode_t mask = umask(0); |
3481 | if (mknod(dev, S_IFCHR | 0666, makedev(10, 229))) { |
3482 | ntfs_log_perror("Failed to create '%s'", dev); |
3483 | if (errno == EPERM) |
3484 | ntfs_log_error("%s", dev_fuse_msg); |
3485 | } |
3486 | umask(mask); |
3487 | } |
3488 | } |
3489 | |
3490 | static void create_dev_fuse(void) |
3491 | { |
3492 | mknod_dev_fuse("/dev/fuse"); |
3493 | |
3494 | #ifdef __UCLIBC__ |
3495 | { |
3496 | struct stat st; |
3497 | /* The fuse device is under /dev/misc using devfs. */ |
3498 | if (stat("/dev/misc", &st) && (errno == ENOENT)) { |
3499 | mode_t mask = umask(0); |
3500 | mkdir("/dev/misc", 0775); |
3501 | umask(mask); |
3502 | } |
3503 | mknod_dev_fuse("/dev/misc/fuse"); |
3504 | } |
3505 | #endif |
3506 | } |
3507 | |
3508 | static fuse_fstype get_fuse_fstype(void) |
3509 | { |
3510 | char buf[256]; |
3511 | fuse_fstype fstype = FSTYPE_NONE; |
3512 | |
3513 | FILE *f = fopen("/proc/filesystems", "r"); |
3514 | if (!f) { |
3515 | ntfs_log_perror("Failed to open /proc/filesystems"); |
3516 | return FSTYPE_UNKNOWN; |
3517 | } |
3518 | |
3519 | while (fgets(buf, sizeof(buf), f)) { |
3520 | if (strstr(buf, "fuseblk\n")) { |
3521 | fstype = FSTYPE_FUSEBLK; |
3522 | break; |
3523 | } |
3524 | if (strstr(buf, "fuse\n")) |
3525 | fstype = FSTYPE_FUSE; |
3526 | } |
3527 | |
3528 | fclose(f); |
3529 | return fstype; |
3530 | } |
3531 | |
3532 | static fuse_fstype load_fuse_module(void) |
3533 | { |
3534 | int i; |
3535 | struct stat st; |
3536 | pid_t pid; |
3537 | const char *cmd = "/sbin/modprobe"; |
3538 | struct timespec req = { 0, 100000000 }; /* 100 msec */ |
3539 | fuse_fstype fstype; |
3540 | |
3541 | if (!stat(cmd, &st) && !geteuid()) { |
3542 | pid = fork(); |
3543 | if (!pid) { |
3544 | execl(cmd, cmd, "fuse", NULL); |
3545 | _exit(1); |
3546 | } else if (pid != -1) |
3547 | waitpid(pid, NULL, 0); |
3548 | } |
3549 | |
3550 | for (i = 0; i < 10; i++) { |
3551 | /* |
3552 | * We sleep first because despite the detection of the loaded |
3553 | * FUSE kernel module, fuse_mount() can still fail if it's not |
3554 | * fully functional/initialized. Note, of course this is still |
3555 | * unreliable but usually helps. |
3556 | */ |
3557 | nanosleep(&req, NULL); |
3558 | fstype = get_fuse_fstype(); |
3559 | if (fstype != FSTYPE_NONE) |
3560 | break; |
3561 | } |
3562 | return fstype; |
3563 | } |
3564 | |
3565 | #endif |
3566 | |
3567 | static struct fuse_chan *try_fuse_mount(char *parsed_options) |
3568 | { |
3569 | struct fuse_chan *fc = NULL; |
3570 | struct fuse_args margs = FUSE_ARGS_INIT(0, NULL); |
3571 | |
3572 | /* The fuse_mount() options get modified, so we always rebuild it */ |
3573 | if ((fuse_opt_add_arg(&margs, EXEC_NAME) == -1 || |
3574 | fuse_opt_add_arg(&margs, "-o") == -1 || |
3575 | fuse_opt_add_arg(&margs, parsed_options) == -1)) { |
3576 | ntfs_log_error("Failed to set FUSE options.\n"); |
3577 | goto free_args; |
3578 | } |
3579 | |
3580 | fc = fuse_mount(opts.mnt_point, &margs); |
3581 | free_args: |
3582 | fuse_opt_free_args(&margs); |
3583 | return fc; |
3584 | |
3585 | } |
3586 | |
3587 | static int set_fuseblk_options(char **parsed_options) |
3588 | { |
3589 | char options[64]; |
3590 | long pagesize; |
3591 | u32 blksize = ctx->vol->cluster_size; |
3592 | |
3593 | pagesize = sysconf(_SC_PAGESIZE); |
3594 | if (pagesize < 1) |
3595 | pagesize = 4096; |
3596 | |
3597 | if (blksize > (u32)pagesize) |
3598 | blksize = pagesize; |
3599 | |
3600 | snprintf(options, sizeof(options), ",blkdev,blksize=%u", blksize); |
3601 | if (ntfs_strappend(parsed_options, options)) |
3602 | return -1; |
3603 | return 0; |
3604 | } |
3605 | |
3606 | static struct fuse *mount_fuse(char *parsed_options) |
3607 | { |
3608 | struct fuse *fh = NULL; |
3609 | struct fuse_args args = FUSE_ARGS_INIT(0, NULL); |
3610 | |
3611 | ctx->fc = try_fuse_mount(parsed_options); |
3612 | if (!ctx->fc) |
3613 | return NULL; |
3614 | |
3615 | if (fuse_opt_add_arg(&args, "") == -1) |
3616 | goto err; |
3617 | #if !CACHEING |
3618 | if (fuse_opt_add_arg(&args, "-ouse_ino,kernel_cache,attr_timeout=0") == -1) |
3619 | goto err; |
3620 | #else |
3621 | if (fuse_opt_add_arg(&args, "-ouse_ino,kernel_cache,attr_timeout=1") == -1) |
3622 | goto err; |
3623 | #endif |
3624 | if (ctx->debug) |
3625 | if (fuse_opt_add_arg(&args, "-odebug") == -1) |
3626 | goto err; |
3627 | |
3628 | fh = fuse_new(ctx->fc, &args , &ntfs_3g_ops, sizeof(ntfs_3g_ops), NULL); |
3629 | if (!fh) |
3630 | goto err; |
3631 | |
3632 | if (fuse_set_signal_handlers(fuse_get_session(fh))) |
3633 | goto err_destory; |
3634 | out: |
3635 | fuse_opt_free_args(&args); |
3636 | return fh; |
3637 | err_destory: |
3638 | fuse_destroy(fh); |
3639 | fh = NULL; |
3640 | err: |
3641 | fuse_unmount(opts.mnt_point, ctx->fc); |
3642 | goto out; |
3643 | } |
3644 | |
3645 | static void setup_logging(char *parsed_options) |
3646 | { |
3647 | if (!ctx->no_detach) { |
3648 | if (daemon(0, ctx->debug)) |
3649 | ntfs_log_error("Failed to daemonize.\n"); |
3650 | else if (!ctx->debug) { |
3651 | #ifndef DEBUG |
3652 | ntfs_log_set_handler(ntfs_log_handler_syslog); |
3653 | /* Override default libntfs identify. */ |
3654 | openlog(EXEC_NAME, LOG_PID, LOG_DAEMON); |
3655 | #endif |
3656 | } |
3657 | } |
3658 | |
3659 | ctx->seccache = (struct PERMISSIONS_CACHE*)NULL; |
3660 | |
3661 | ntfs_log_info("Version %s %s %d\n", VERSION, FUSE_TYPE, fuse_version()); |
3662 | if (strcmp(opts.arg_device,opts.device)) |
3663 | ntfs_log_info("Requested device %s canonicalized as %s\n", |
3664 | opts.arg_device,opts.device); |
3665 | ntfs_log_info("Mounted %s (%s, label \"%s\", NTFS %d.%d)\n", |
3666 | opts.device, (ctx->ro) ? "Read-Only" : "Read-Write", |
3667 | ctx->vol->vol_name, ctx->vol->major_ver, |
3668 | ctx->vol->minor_ver); |
3669 | ntfs_log_info("Cmdline options: %s\n", opts.options ? opts.options : ""); |
3670 | ntfs_log_info("Mount options: %s\n", parsed_options); |
3671 | } |
3672 | |
3673 | int main(int argc, char *argv[]) |
3674 | { |
3675 | char *parsed_options = NULL; |
3676 | struct fuse *fh; |
3677 | #if !(defined(__sun) && defined (__SVR4)) |
3678 | fuse_fstype fstype = FSTYPE_UNKNOWN; |
3679 | #endif |
3680 | const char *permissions_mode = (const char*)NULL; |
3681 | const char *failed_secure = (const char*)NULL; |
3682 | #if defined(HAVE_SETXATTR) && defined(XATTR_MAPPINGS) |
3683 | struct XATTRMAPPING *xattr_mapping = (struct XATTRMAPPING*)NULL; |
3684 | #endif /* defined(HAVE_SETXATTR) && defined(XATTR_MAPPINGS) */ |
3685 | struct stat sbuf; |
3686 | unsigned long existing_mount; |
3687 | int err, fd; |
3688 | |
3689 | /* |
3690 | * Make sure file descriptors 0, 1 and 2 are open, |
3691 | * otherwise chaos would ensue. |
3692 | */ |
3693 | do { |
3694 | fd = open("/dev/null", O_RDWR); |
3695 | if (fd > 2) |
3696 | close(fd); |
3697 | } while (fd >= 0 && fd <= 2); |
3698 | |
3699 | #ifndef FUSE_INTERNAL |
3700 | if ((getuid() != geteuid()) || (getgid() != getegid())) { |
3701 | fprintf(stderr, "%s", setuid_msg); |
3702 | return NTFS_VOLUME_INSECURE; |
3703 | } |
3704 | #endif |
3705 | if (drop_privs()) |
3706 | return NTFS_VOLUME_NO_PRIVILEGE; |
3707 | |
3708 | ntfs_set_locale(); |
3709 | ntfs_log_set_handler(ntfs_log_handler_stderr); |
3710 | |
3711 | if (ntfs_parse_options(&opts, usage, argc, argv)) { |
3712 | usage(); |
3713 | return NTFS_VOLUME_SYNTAX_ERROR; |
3714 | } |
3715 | |
3716 | if (ntfs_fuse_init()) { |
3717 | err = NTFS_VOLUME_OUT_OF_MEMORY; |
3718 | goto err2; |
3719 | } |
3720 | |
3721 | parsed_options = parse_mount_options(ctx, &opts, FALSE); |
3722 | if (!parsed_options) { |
3723 | err = NTFS_VOLUME_SYNTAX_ERROR; |
3724 | goto err_out; |
3725 | } |
3726 | if (!ntfs_check_if_mounted(opts.device,&existing_mount) |
3727 | && (existing_mount & NTFS_MF_MOUNTED) |
3728 | /* accept multiple read-only mounts */ |
3729 | && (!(existing_mount & NTFS_MF_READONLY) || !ctx->ro)) { |
3730 | err = NTFS_VOLUME_LOCKED; |
3731 | goto err_out; |
3732 | } |
3733 | |
3734 | /* need absolute mount point for junctions */ |
3735 | if (opts.mnt_point[0] == '/') |
3736 | ctx->abs_mnt_point = strdup(opts.mnt_point); |
3737 | else { |
3738 | ctx->abs_mnt_point = (char*)ntfs_malloc(PATH_MAX); |
3739 | if (ctx->abs_mnt_point) { |
3740 | if (getcwd(ctx->abs_mnt_point, |
3741 | PATH_MAX - strlen(opts.mnt_point) - 1)) { |
3742 | strcat(ctx->abs_mnt_point, "/"); |
3743 | strcat(ctx->abs_mnt_point, opts.mnt_point); |
3744 | #if defined(__sun) && defined (__SVR4) |
3745 | /* Solaris also wants the absolute mount point */ |
3746 | opts.mnt_point = ctx->abs_mnt_point; |
3747 | #endif /* defined(__sun) && defined (__SVR4) */ |
3748 | } |
3749 | } |
3750 | } |
3751 | if (!ctx->abs_mnt_point) { |
3752 | err = NTFS_VOLUME_OUT_OF_MEMORY; |
3753 | goto err_out; |
3754 | } |
3755 | |
3756 | ctx->security.uid = 0; |
3757 | ctx->security.gid = 0; |
3758 | if ((opts.mnt_point[0] == '/') |
3759 | && !stat(opts.mnt_point,&sbuf)) { |
3760 | /* collect owner of mount point, useful for default mapping */ |
3761 | ctx->security.uid = sbuf.st_uid; |
3762 | ctx->security.gid = sbuf.st_gid; |
3763 | } |
3764 | |
3765 | #if defined(linux) || defined(__uClinux__) |
3766 | fstype = get_fuse_fstype(); |
3767 | |
3768 | err = NTFS_VOLUME_NO_PRIVILEGE; |
3769 | if (restore_privs()) |
3770 | goto err_out; |
3771 | |
3772 | if (fstype == FSTYPE_NONE || fstype == FSTYPE_UNKNOWN) |
3773 | fstype = load_fuse_module(); |
3774 | create_dev_fuse(); |
3775 | |
3776 | if (drop_privs()) |
3777 | goto err_out; |
3778 | #endif |
3779 | if (stat(opts.device, &sbuf)) { |
3780 | ntfs_log_perror("Failed to access '%s'", opts.device); |
3781 | err = NTFS_VOLUME_NO_PRIVILEGE; |
3782 | goto err_out; |
3783 | } |
3784 | |
3785 | #if !(defined(__sun) && defined (__SVR4)) |
3786 | /* Always use fuseblk for block devices unless it's surely missing. */ |
3787 | if (S_ISBLK(sbuf.st_mode) && (fstype != FSTYPE_FUSE)) |
3788 | ctx->blkdev = TRUE; |
3789 | #endif |
3790 | |
3791 | #ifndef FUSE_INTERNAL |
3792 | if (getuid() && ctx->blkdev) { |
3793 | ntfs_log_error("%s", unpriv_fuseblk_msg); |
3794 | err = NTFS_VOLUME_NO_PRIVILEGE; |
3795 | goto err2; |
3796 | } |
3797 | #endif |
3798 | err = ntfs_open(opts.device); |
3799 | if (err) |
3800 | goto err_out; |
3801 | |
3802 | /* Force read-only mount if the device was found read-only */ |
3803 | if (!ctx->ro && NVolReadOnly(ctx->vol)) { |
3804 | ctx->ro = TRUE; |
3805 | if (ntfs_strinsert(&parsed_options, ",ro")) |
3806 | goto err_out; |
3807 | } |
3808 | /* We must do this after ntfs_open() to be able to set the blksize */ |
3809 | if (ctx->blkdev && set_fuseblk_options(&parsed_options)) |
3810 | goto err_out; |
3811 | |
3812 | ctx->security.vol = ctx->vol; |
3813 | ctx->vol->secure_flags = ctx->secure_flags; |
3814 | #ifdef HAVE_SETXATTR /* extended attributes interface required */ |
3815 | ctx->vol->efs_raw = ctx->efs_raw; |
3816 | #endif /* HAVE_SETXATTR */ |
3817 | /* JPA open $Secure, (whatever NTFS version !) */ |
3818 | /* to initialize security data */ |
3819 | if (ntfs_open_secure(ctx->vol) && (ctx->vol->major_ver >= 3)) |
3820 | failed_secure = "Could not open file $Secure"; |
3821 | if (!ntfs_build_mapping(&ctx->security,ctx->usermap_path, |
3822 | (ctx->vol->secure_flags |
3823 | & ((1 << SECURITY_DEFAULT) | (1 << SECURITY_ACL))) |
3824 | && !(ctx->vol->secure_flags & (1 << SECURITY_WANTED)))) { |
3825 | #if POSIXACLS |
3826 | /* use basic permissions if requested */ |
3827 | if (ctx->vol->secure_flags & (1 << SECURITY_DEFAULT)) |
3828 | permissions_mode = "User mapping built, Posix ACLs not used"; |
3829 | else { |
3830 | permissions_mode = "User mapping built, Posix ACLs in use"; |
3831 | #if KERNELACLS |
3832 | if (ntfs_strinsert(&parsed_options, ",default_permissions,acl")) { |
3833 | err = NTFS_VOLUME_SYNTAX_ERROR; |
3834 | goto err_out; |
3835 | } |
3836 | #endif /* KERNELACLS */ |
3837 | } |
3838 | #else /* POSIXACLS */ |
3839 | #if KERNELPERMS |
3840 | if (!(ctx->vol->secure_flags |
3841 | & ((1 << SECURITY_DEFAULT) | (1 << SECURITY_ACL)))) { |
3842 | /* |
3843 | * No explicit option but user mapping found |
3844 | * force default security |
3845 | */ |
3846 | ctx->vol->secure_flags |= (1 << SECURITY_DEFAULT); |
3847 | if (ntfs_strinsert(&parsed_options, ",default_permissions")) { |
3848 | err = NTFS_VOLUME_SYNTAX_ERROR; |
3849 | goto err_out; |
3850 | } |
3851 | } |
3852 | #endif /* KERNELPERMS */ |
3853 | permissions_mode = "User mapping built"; |
3854 | #endif /* POSIXACLS */ |
3855 | } else { |
3856 | ctx->security.uid = ctx->uid; |
3857 | ctx->security.gid = ctx->gid; |
3858 | /* same ownership/permissions for all files */ |
3859 | ctx->security.mapping[MAPUSERS] = (struct MAPPING*)NULL; |
3860 | ctx->security.mapping[MAPGROUPS] = (struct MAPPING*)NULL; |
3861 | if ((ctx->vol->secure_flags & (1 << SECURITY_WANTED)) |
3862 | && !(ctx->vol->secure_flags & (1 << SECURITY_DEFAULT))) { |
3863 | ctx->vol->secure_flags |= (1 << SECURITY_DEFAULT); |
3864 | if (ntfs_strinsert(&parsed_options, ",default_permissions")) { |
3865 | err = NTFS_VOLUME_SYNTAX_ERROR; |
3866 | goto err_out; |
3867 | } |
3868 | } |
3869 | if (ctx->vol->secure_flags & (1 << SECURITY_DEFAULT)) { |
3870 | ctx->vol->secure_flags |= (1 << SECURITY_RAW); |
3871 | permissions_mode = "Global ownership and permissions enforced"; |
3872 | } else { |
3873 | ctx->vol->secure_flags &= ~(1 << SECURITY_RAW); |
3874 | permissions_mode = "Ownership and permissions disabled"; |
3875 | } |
3876 | } |
3877 | if (ctx->usermap_path) |
3878 | free (ctx->usermap_path); |
3879 | |
3880 | #if defined(HAVE_SETXATTR) && defined(XATTR_MAPPINGS) |
3881 | xattr_mapping = ntfs_xattr_build_mapping(ctx->vol, |
3882 | ctx->xattrmap_path); |
3883 | ctx->vol->xattr_mapping = xattr_mapping; |
3884 | /* |
3885 | * Errors are logged, do not refuse mounting, it would be |
3886 | * too difficult to fix the unmountable mapping file. |
3887 | */ |
3888 | if (ctx->xattrmap_path) |
3889 | free(ctx->xattrmap_path); |
3890 | #endif /* defined(HAVE_SETXATTR) && defined(XATTR_MAPPINGS) */ |
3891 | |
3892 | fh = mount_fuse(parsed_options); |
3893 | if (!fh) { |
3894 | err = NTFS_VOLUME_FUSE_ERROR; |
3895 | goto err_out; |
3896 | } |
3897 | |
3898 | ctx->mounted = TRUE; |
3899 | |
3900 | #if defined(linux) || defined(__uClinux__) |
3901 | if (S_ISBLK(sbuf.st_mode) && (fstype == FSTYPE_FUSE)) |
3902 | ntfs_log_info("%s", fuse26_kmod_msg); |
3903 | #endif |
3904 | setup_logging(parsed_options); |
3905 | if (failed_secure) |
3906 | ntfs_log_info("%s\n",failed_secure); |
3907 | if (permissions_mode) |
3908 | ntfs_log_info("%s, configuration type %d\n",permissions_mode, |
3909 | 4 + POSIXACLS*6 - KERNELPERMS*3 + CACHEING); |
3910 | if ((ctx->vol->secure_flags & (1 << SECURITY_RAW)) |
3911 | && !ctx->uid && ctx->gid) |
3912 | ntfs_log_error("Warning : using problematic uid==0 and gid!=0\n"); |
3913 | |
3914 | fuse_loop(fh); |
3915 | |
3916 | err = 0; |
3917 | |
3918 | fuse_unmount(opts.mnt_point, ctx->fc); |
3919 | fuse_destroy(fh); |
3920 | err_out: |
3921 | ntfs_mount_error(opts.device, opts.mnt_point, err); |
3922 | if (ctx->abs_mnt_point) |
3923 | free(ctx->abs_mnt_point); |
3924 | #if defined(HAVE_SETXATTR) && defined(XATTR_MAPPINGS) |
3925 | ntfs_xattr_free_mapping(xattr_mapping); |
3926 | #endif /* defined(HAVE_SETXATTR) && defined(XATTR_MAPPINGS) */ |
3927 | err2: |
3928 | ntfs_close(); |
3929 | free(ctx); |
3930 | free(parsed_options); |
3931 | free(opts.options); |
3932 | free(opts.device); |
3933 | return err; |
3934 | } |
3935 |