-
Notifications
You must be signed in to change notification settings - Fork 12
/
Copy pathdir.c
1469 lines (1357 loc) · 47.6 KB
/
dir.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/**
* dir.c - NTFS Driver by AVM GmbH (ANTFS)
* Based on ntfs-3g
*
* Copyright (c) 2016 Martin Pommerenke, Jens Krieg, Arwed Meyer,
* Christian René Sechting
*
* This file is originated from the Linux-NTFS project.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "antfs.h"
#include <linux/pagemap.h>
#include <linux/file.h>
#include <linux/sched.h>
#include <linux/namei.h>
#include <linux/slab.h>
#include "misc.h"
#include "dir.h"
#include "reparse.h"
/**
* Check if we are in the /FRITZ directory.
*
* @entry dentry to check
*
* Returns @TRUE if the dentry is inside a folder named "FRITZ" in the disks
* root.
*/
static bool in_fritz_dir(struct dentry *entry)
{
struct dentry *tmp_entry = entry;
struct dentry *prev_entry = NULL;
/* walk up */
while (tmp_entry->d_parent && tmp_entry->d_parent != tmp_entry) {
prev_entry = tmp_entry;
tmp_entry = tmp_entry->d_parent;
}
/* FRITZ dir? */
return prev_entry && !strcmp(prev_entry->d_name.name, "FRITZ");
}
/**
* create a link from @entry to the filesystem root
*
* @entry dentry to check
* @mnt_point result string
* @size size of @mnt_point
*
* returns 0 on success and -EINVAL if @mnt_point is to small
*/
static int antfs_root_path(struct dentry *entry, char *mnt_point,
size_t size)
{
struct dentry *tmp_entry = entry->d_parent;
/* walk up */
while (tmp_entry->d_parent && tmp_entry->d_parent != tmp_entry) {
tmp_entry = tmp_entry->d_parent;
/* check if we still have enough space */
if (strlen(mnt_point) >= size - 4)
return -EINVAL;
strlcat(mnt_point, "../", size);
}
return 0;
}
/**
* @brief antfs_lookup checks if an entry is kept inside a directory.
*
* @param dir the directory's inode we are looking in for the entry
* @param entry the entry we are looking for
* @param flags TODO: idk what these are for even ext2 doesnt use them...
*
* @return the dentry we looked for which is now connected to its corresponding
* vfs inode if everything worked out. Errorcode pointer if something
* went wrong.
*
* antfs_lookup gets called by the vfs to find an entry inside a given
* directory. It finds the corresponding ntfs_inode to the entry in question,
* and if it is an actual file/directory and not the root node creates the new
* vfs inode. The dentry gets connected to the newly created vfs inode in form
* of a new dentry which the argument dentry should point to and gets returned.
* In case of an error during the connection of inode and dentry the error code
* gets returned for the caller to handle. If the name of the entry to look for
* exceeds 1024 characters it returns the -ENAMETOOLONG error.
* In case the entry in question is not existent in the given directory the
* argument pointer to the entry to look for stays untouched and NULL gets
* returned. In case of a ls call the returned NULL pointer will signalize that
* there is no entry with that name, but in case of an mkdir call the untouched
* argument pointer combined with the returned NULL pointer signalises that a
* new file/directory with that name we were looking for can be created.
* We need to feed the ntfs inode to the cache in case we just newly created
* it. Since we can't be sure about that antfs_check_feed_cache() is used.
*/
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)
static struct dentry *antfs_lookup(struct inode *dir, struct dentry *entry,
struct nameidata *nd)
#else
static struct dentry *antfs_lookup(struct inode *dir, struct dentry *entry,
unsigned int flags __attribute__((unused)))
#endif
{
struct dentry *newent;
struct inode *inode = NULL;
struct ntfs_inode *dir_ni = ANTFS_NI(dir);
struct antfs_sb_info *sbi = ANTFS_SB(dir->i_sb);
struct ntfs_inode *ni;
struct FILE_NAME_ATTR *fn;
int err;
ntfschar *unicode = NULL;
u64 inum;
antfs_log_enter("dir ino: %lu; dentry: %s", (unsigned long)dir->i_ino,
entry->d_name.name);
if (entry->d_name.len > 1024) {
err = -ENAMETOOLONG;
goto out_err;
}
if (mutex_lock_interruptible_nested(&dir_ni->ni_lock,
NI_MUTEX_NORMAL)) {
err = -ERESTARTSYS;
goto out_err;
}
err = ntfs_mbstoucs(entry->d_name.name, &unicode);
if (err < 0) {
antfs_log_error("Could not convert filename to Unicode:"
" '%s'", entry->d_name.name);
goto out_err_locked;
}
if (err > NTFS_MAX_NAME_LEN) {
ntfs_free(unicode);
err = -ENAMETOOLONG;
goto out_err_locked;
}
err = ntfs_inode_lookup_by_name(dir_ni, unicode, err, &inum, &fn);
ntfs_free(unicode);
if (err) {
antfs_log_debug("Couldn't find name '%s'.", entry->d_name.name);
if (err != -ENOENT)
goto out_err_locked;
mutex_unlock(&dir_ni->ni_lock);
} else {
inum = MREF(inum);
ni = ntfs_inode_open(sbi->vol, inum, fn);
if (IS_ERR(ni)) {
err = PTR_ERR(ni);
/* -ENOENT or -EIO should hint to permanent errors
* on disk. -EIO could also mean we cannot access
* physical media, but in this case there isn't
* likely anything left we could damage.
*
* To be even more cautious, only delete stuff in
* FRITZ directory.
*/
if ((err == -ENOENT || err == -EIO) &&
in_fritz_dir(entry)) {
antfs_log_error("Cannot open inode %llu, "
"err %d. Deleting dentry.",
(unsigned long long)inum, err);
/* We have an orphaned dentry.
* Kill it with fire!
*/
err = ntfs_index_remove(dir_ni, NULL, fn,
(fn->file_name_length * 2) +
sizeof(*fn));
ntfs_inode_sync(dir_ni);
mutex_unlock(&dir_ni->ni_lock);
ntfs_free(fn);
if (err)
antfs_log_error("Removing orphaned "
"index failed with %d",
err);
} else {
mutex_unlock(&dir_ni->ni_lock);
ntfs_free(fn);
}
} else {
mutex_unlock(&dir_ni->ni_lock);
ntfs_free(fn);
if (ni->mft_no == FILE_ROOT) {
err = -EINVAL;
goto out_err_free_ni;
}
inode = ANTFS_I(ni);
}
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0)
newent = d_materialise_unique(entry, inode);
#else
newent = d_splice_alias(inode, entry);
#endif
if (IS_ERR(newent)) {
err = PTR_ERR(newent);
goto out_err;
}
antfs_log_leave("ok");
return newent;
out_err_locked:
mutex_unlock(&dir_ni->ni_lock);
out_err:
antfs_log_leave("err: %d", err);
return ERR_PTR(err);
out_err_free_ni:
ntfs_inode_close(ni);
goto out_err;
}
/**
* @brief creates a new file/symlink/directory
*
* @param dir parent directory to create the new file/symlink/directory in
* @param entry entry for the new file/symlink/directory
* @param mode i_mode that should be set for the new file/symlink/directory
*
* @return 0 if everything is ok, error code otherwise
*
* antfs_create is creating a new file/symlink/directory on the ntfs device
* @dir resides on. The creation happens in two steps, first the new file is
* created on the ntfs device and stored there. For that the name has to be
* transformed into an unicode string. After that, the ntfs inode and its
* corresponding vfs inode will be allocated and updated. After all zero will
* be returned. This function is called by antfs_create to create regular files
* and from antfs_mkdir to create directories.
*/
static int antfs_create_i(struct inode *dir, struct dentry *entry, int mode)
{
struct ntfs_inode *dir_ni = ANTFS_NI(dir);
struct inode *inode = NULL;
struct ntfs_inode *ni;
ntfschar *uname = NULL;
le32 securid = 0;
int uname_len;
int err = 0;
antfs_log_enter("%s", entry->d_name.name);
/* - check if the file already exists or no creation allowed - */
if (entry->d_inode) {
err = -EEXIST;
goto out;
}
if (dir_ni->mft_no == FILE_EXTEND) {
antfs_log_debug("Deny creating files in $Extend");
err = -EPERM;
goto out;
}
/* - we are going to create a new file - */
uname_len = ntfs_mbstoucs(entry->d_name.name, &uname);
if (uname_len < 0) {
antfs_log_error("Could not convert name to ucs");
err = uname_len;
goto out;
}
if (mutex_lock_interruptible_nested(&dir_ni->ni_lock,
NI_MUTEX_PARENT)) {
err = -ERESTARTSYS;
goto out;
}
ni = ntfs_create(dir_ni, securid, uname, uname_len, mode & S_IFMT);
mutex_unlock(&dir_ni->ni_lock);
if (IS_ERR(ni)) {
err = PTR_ERR(ni);
antfs_log_info("Could not acquire ni: err=%d", err);
goto free_name;
}
inode = ANTFS_I(ni);
ni->flags |= FILE_ATTR_ARCHIVE;
/* - write dir back to disk - */
err = antfs_inode_init(inode);
if (err) {
antfs_log_error("Could not fetch VFS inode!");
/* - we have to rewind the allocated ni - */
if (mutex_lock_interruptible_nested(&ni->ni_lock,
NI_MUTEX_NORMAL)) {
err = -ERESTARTSYS;
goto free_name;
}
if (mutex_lock_interruptible_nested(&dir_ni->ni_lock,
NI_MUTEX_PARENT)) {
mutex_unlock(&ni->ni_lock);
err = -ERESTARTSYS;
goto free_name;
}
ntfs_unlink(dir_ni->vol, ni, dir_ni, uname, uname_len);
mutex_unlock(&dir_ni->ni_lock);
mutex_unlock(&ni->ni_lock);
clear_nlink(inode);
iget_failed(inode);
goto free_name;
} else {
/* the index_root attribute changed it's data_size, we have to
* pass that to the vfs inode's i_size
*/
i_size_write(dir, ANTFS_NA(dir_ni)->data_size);
ntfs_inode_mark_dirty(ni);
ntfs_inode_mark_dirty(dir_ni);
mark_inode_dirty(inode);
mark_inode_dirty(dir);
}
/* - finish vfs inode! even if we fail to open,
* the file exists now -
*/
d_instantiate(entry, inode);
free_name:
ntfs_free(uname);
out:
antfs_log_leave("inode: %ld; err: %d", inode ? inode->i_ino : -1, err);
return err;
}
/**
* @brief creates a new file/symlink/directory
*
* @param dir parent directory to create the new file in
* @param entry entry for the new file
* @param mode i_mode that should be set for the new file
* @param excl TODO
*
* @return 0 if everything is ok, error code otherwise
*
* antfs_create is creating a new regular file on the ntfs device
* @dir resides on. The creation happens in two steps, first the new file is
* created on the ntfs device and stored there. For that the name has to be
* transformed into an unicode string. After that, the ntfs inode and its
* corresponding vfs inode will be allocated and updated. After all zero will
* be returned.
*/
#if KERNEL_VERSION(3, 3, 0) > LINUX_VERSION_CODE
static int antfs_create(struct inode *dir, struct dentry *entry, int mode,
struct nameidata *nd __attribute__ ((unused)))
#elif KERNEL_VERSION(3, 6, 0) > LINUX_VERSION_CODE
static int antfs_create(struct inode *dir, struct dentry *entry, umode_t mode,
struct nameidata *nd __attribute__ ((unused)))
#else
static int antfs_create(struct inode *dir, struct dentry *entry, umode_t mode,
bool excl __attribute__((unused)))
#endif
{
return antfs_create_i(dir, entry, mode | S_IFREG);
}
/**
* @brief creates a new directory.
*
* @param dir parent directory we want to create a new directory in
* @param entry the dentry for the new directory to create
* @mode the mode in which the new directory can be accessed
*
* @return 0 if everything worked out and the new directory is created.
* error codes in case something went wrong.
*
* After the vfs checked if the name for the new directory is not already in
* use antfs_mkdir gets called to actually create a vfs inode and its
* corresponding ntfs_inode which will be written onto the ntfs device. For
* that, the name provided by the entry has to be converted into a unicode
* string since thats the way ntfs stores file names.
* Access permissions need to be set up according to the ntfs standard as a
* le32 integer and ntfs_create() gets called with the parent directory's
* ntfs_inode, the access permissions, name, name length and directory type.
* If everything worked out the ntfs_inode's flags need to be set with the
* FILE_ATTR_ARCHIVE flag, the atime, mtime and ctime need to be set for
* both parent and new directory.
* We have to push the new ntfs inode onto the ntfs inode cache. This is
* important because ntfs_inode_sync() tries to open the parent directory of
* the file to sync. A new ntfs inode would be allocated if there is no entry
* for that inode in the cache. That would lead to inconsistent ntfs inodes
* since we always keep an open ntfs inode for every vfs inode. The result
* would be a corrupted filesystem. Therefore we need to use the same ntfs
* inode for the same mft_no at all times! Even tho that means that some other
* process could work on the same inode at the same time.
* TODO CRS: change description... too much has changed!
* Creating the vfs inode seems trivial because the corresponding vfs inode
* will always have the same i_ino as the ntfs inode's mft_no. So if we find
* a ntfs inode, we will have a free vfs inode for that. But in rare instances
* we could allocate a just released ntfs inode which still holds a vfs inode
* which needs to be released as well. This case is handled in
* antfs_inode_init(). But in case we can't resolve that problem here, we have
* to rewind the changes we made and wrote back on the disk. This should only
* happen with a very high load so that the process removing the vfs inode gets
* stalled for a longer time. We should not keep our lock forever, so we gently
* fail antfs_mkdir.
* Lastly the ntfs inode gets written back to the disk and the vfs inode gets
* connected to the dentry.
*/
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
static int antfs_mkdir(struct inode *dir, struct dentry *entry, int mode)
#else
static int antfs_mkdir(struct inode *dir, struct dentry *entry, umode_t mode)
#endif
{
return antfs_create_i(dir, entry, mode | S_IFDIR);
}
/**
* @brief removes a file
*
* @param dir parent directory of the file to delete
* @param entry corresponding dentry to the file to delete
*
* @return 0 if @entry was successfully deleted, error code otherwise
*
* antfs_unlinnk deletes a file from the ntfs device. To use the ntfs-3g lib
* function ntfs_delete() we need to translate the name of the directory into an
* unicode string. Before we can remove the file from the disk we have to remove
* the cache entry. The removal of the file from the ntfs device is then
* done by ntfs_delete().
*/
static int antfs_unlink(struct inode *dir, struct dentry *entry)
{
struct inode *inode = entry->d_inode;
struct antfs_sb_info *sbi = ANTFS_SB(dir->i_sb);
struct ntfs_inode *ni = ANTFS_NI(inode);
struct ntfs_inode *dir_ni;
ntfschar *uname = NULL;
int uname_len;
int err = 0;
antfs_log_enter();
if (ni->mft_no < FILE_FIRST_USER) {
err = -EPERM;
antfs_log_error("Wrong arguments");
goto out;
}
dir_ni = ANTFS_NI(dir);
if (dir_ni->mft_no == FILE_EXTEND) {
err = -EPERM;
antfs_log_debug("deny unlinking metadata files from $Extend");
goto out;
}
uname_len = ntfs_mbstoucs(entry->d_name.name, &uname);
if (uname_len < 0) {
antfs_log_error("Could not convert ucs to char *");
err = uname_len;
goto out;
}
if (mutex_lock_interruptible_nested(&ni->ni_lock, NI_MUTEX_NORMAL)) {
err = -ERESTARTSYS;
goto out;
}
if (mutex_lock_interruptible_nested(&dir_ni->ni_lock,
NI_MUTEX_PARENT)) {
mutex_unlock(&ni->ni_lock);
err = -ERESTARTSYS;
goto out;
}
/* - unlink inode on disk - */
err = ntfs_unlink(sbi->vol, ni, dir_ni, uname, uname_len);
mutex_unlock(&dir_ni->ni_lock);
mutex_unlock(&ni->ni_lock);
if (err) {
antfs_log_error("Something went wrong while deleting: %d", err);
goto free_name;
}
/* - prepare nii for the vfs inode's destruction and sync dir_ni - */
/* the index_root attribute changed it's data_size, we have to
* pass that to the vfs inode's i_size
*/
i_size_write(dir, ANTFS_NA(dir_ni)->data_size);
ntfs_inode_mark_dirty(dir_ni);
mark_inode_dirty(dir);
if (unlikely(ni->mrec->link_count))
ntfs_inode_mark_dirty(ni);
inode->i_ctime = dir->i_ctime;
/* decrementing the linkcount marks the inode dirty */
inode_dec_link_count(inode);
free_name:
ntfs_free(uname);
out:
return err;
}
/**
* @brief removes a directory
*
* @param dir parent directory of the directory to delete
* @param entry corresponding dentry to the directory to delete
*
* @return 0 if @entry was successfully deleted, error code otherwise
*
* antfs_rmdir deletes a directory from the ntfs device. To use the ntfs-3g
* lib function ntfs_delete() we need to translate the name of the directory
* into an unicode string. Before the ntfs inode can be deleted off the disk,
* we have to remove the cache entry of that ntfs inode. Removing the
* directory from the ntfs device is then done by ntfs_delete().
*/
static int antfs_rmdir(struct inode *dir, struct dentry *entry)
{
struct inode *inode = entry->d_inode;
struct antfs_sb_info *sbi = ANTFS_SB(dir->i_sb);
struct ntfs_inode *ni = ANTFS_NI(inode);
struct ntfs_inode *dir_ni;
ntfschar *uname = NULL;
int err = 0, uname_len;
antfs_log_enter("%s (%llu) from (%lu)", entry->d_name.name, ni->mft_no,
dir->i_ino);
/* - never remove meta data files - */
if (ni->mft_no < FILE_FIRST_USER) {
err = -EPERM;
goto out;
}
dir_ni = ANTFS_NI(dir);
/* - don't try to delete a directory which is not empty - */
err = ntfs_check_empty_dir(ni);
if (err) {
/* TODO: This happens in antfs_mv.sh
* 1. Why does this happen in 1st test cycle:
* What is in index root?
* 2. Make this a warning. */
antfs_log_debug("Directory is not empty. err:%d", err);
goto out;
}
uname_len = ntfs_mbstoucs(entry->d_name.name, &uname);
if (uname_len < 0) {
antfs_log_error("Couldn't convert ucs to char *");
err = uname_len;
goto out;
}
if (mutex_lock_interruptible_nested(&ni->ni_lock, NI_MUTEX_NORMAL)) {
err = -ERESTARTSYS;
goto out;
}
if (mutex_lock_interruptible_nested(&dir_ni->ni_lock,
NI_MUTEX_PARENT)) {
mutex_unlock(&ni->ni_lock);
err = -ERESTARTSYS;
goto out;
}
/* remove the cache entry just before ntfs_delete() so we can be sure
* that we will at leat try to remove it.
*/
err = ntfs_unlink(sbi->vol, ni, dir_ni, uname, uname_len);
mutex_unlock(&dir_ni->ni_lock);
mutex_unlock(&ni->ni_lock);
if (err) {
antfs_log_error("Could not unlink (%llu)", ni->mft_no);
goto free_name;
}
/* the index_root attribute changed it's data_size, we have to
* pass that to the vfs inode's i_size
*/
i_size_write(dir, ANTFS_NA(dir_ni)->data_size);
ntfs_inode_mark_dirty(dir_ni);
mark_inode_dirty(dir);
if (unlikely(ni->mrec->link_count))
ntfs_inode_mark_dirty(ni);
/* - notify vfs about deletion. - */
inode_dec_link_count(inode);
if (inode->i_nlink)
antfs_log_error("i_nlink not 0!");
free_name:
ntfs_free(uname);
out:
antfs_log_leave();
return err;
}
/**
* @brief renames a file and moves it to a new location if needed.
*
* @param olddir inode of the old parent directory
* @param oldent old dentry of the file to rename
* @param newdir inode of the new parent directory
* @param newent new dentry of the file with new name already set
*
* @return 0 if name is changed, error code otherwise
*
* antfs_rename is changing the name of a file/directory. If needed
* the file will be relocated to a new parent directory. Any metadata file
* should not be renamed. If the new entry already has a ntfs inode on the
* device, then we error out since this shouldn't be the case.
* The renaming process is very simple, first we create a new link from the
* new parent directory to the file. Since the file's inode doesn't have a
* name we can just link the existing inode now with a new name. Then only
* the modify and change times need to be adjusted from the new parent dir
* and the change time of the file. The file will have now a new link with
* the desired name and location. At this point @newdir's ntfs inode needs
* to be written back to the disk. Otherwise we would corrupt the inode's
* index header if we would continue without synchronizing.
* The second step is to actually remove the old link to pretent that we
* actually renamed the file, even though in fact we never renamed anything
* but created a new link with the desired name. ntfs_delete() is used to
* remove the unwanted link. We need to synchronize @olddir's und @newent's
* ntfs inode's after ntfs_delete().
* NOTE: that we don't need to remove the cache entry here since we didn't
* completely deleted that file/directory but just remove the link with the
* old name. ntfs_delete() won't go to the point of invalidating the cache
* as long as the link count is greater than 0.
*/
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0)
static int antfs_rename(struct inode *olddir, struct dentry *oldent,
struct inode *newdir, struct dentry *newent,
unsigned int flags)
#else
static int antfs_rename(struct inode *olddir, struct dentry *oldent,
struct inode *newdir, struct dentry *newent)
#endif
{
struct antfs_sb_info *sbi = ANTFS_SB(olddir->i_sb);
struct ntfs_inode *ni, *dir_ni, *check_ni, *old_dir_ni;
ntfschar *uname = NULL, *old_uname = NULL;
int uname_len;
int err = 0;
bool ni_locked = false, dir_ni_locked = false,
old_dir_ni_locked = false;
/* ni is the ntfs inode of the directory that will be renamed
* dir_ni is the ntfs inode of the new parent, which might be the
* same as the old parent.
*/
ni = ANTFS_NI(oldent->d_inode);
dir_ni = ANTFS_NI(newdir);
old_dir_ni = ANTFS_NI(olddir);
antfs_log_enter("%p(%llu) from %p(%lu) to %p(%lu)", ni, ni->mft_no,
dir_ni, olddir->i_ino, ANTFS_NI(newdir), newdir->i_ino);
antfs_log_debug("%s -> %s", oldent->d_name.name, newent->d_name.name);
/* we don't unlink metadata files. Here is the right point of time to
* check that. We also don't want to proceed if our input is broken!
* Either way our arguments are invalid: return EINVAL
*/
if (IS_ERR_OR_NULL(ni) || IS_ERR_OR_NULL(dir_ni)
|| ni->mft_no < FILE_FIRST_USER || (ni->mft_no == dir_ni->mft_no)) {
antfs_log_error("Invalid parameters");
err = -EINVAL;
goto out;
}
if (mutex_lock_interruptible_nested(&ni->ni_lock, NI_MUTEX_NORMAL)) {
err = -ERESTARTSYS;
goto out;
}
ni_locked = true;
if (mutex_lock_interruptible_nested(&dir_ni->ni_lock,
NI_MUTEX_PARENT)) {
err = -ERESTARTSYS;
goto out;
}
dir_ni_locked = true;
if (dir_ni != old_dir_ni) {
if (mutex_lock_interruptible_nested(&old_dir_ni->ni_lock,
NI_MUTEX_PARENT2)) {
err = -ERESTARTSYS;
goto out;
}
old_dir_ni_locked = true;
}
/* - check if the newent already has an existing ni - */
/* TODO: CRS: that should be possible without ntfs_pathname_to_inode */
check_ni = ntfs_pathname_to_inode(sbi->vol, dir_ni,
newent->d_name.name);
if (!IS_ERR(check_ni)) {
/* we could try to do the operation if we are a directory and
* the target is an empty directory, for that we have to:
*/
/*TODO: check if empty, we dont do stuff with not empty check_ni
*TODO: check if newent ni and oldent ni are the same */
/* for now we dont do anything here */
err = -EEXIST;
/* check_ni was opened to verify that there is no record with
* that name already present. In case we actually had a record
* with that name, we now opened it, and it has to be closed
* in order not to leak memory since we lose any reference to
* this check_ni past this function
*/
iput(ANTFS_I(check_ni));
goto out;
}
/* - link the old directory to the new name - */
uname_len = ntfs_mbstoucs(newent->d_name.name, &uname);
if (uname_len < 0) {
err = uname_len;
goto out;
}
/* we want the parent directory of @newent to link towards the @oldent
* with @newent's ucs name. After this step @newent will appear in
* @newdir as a subdirectory pointing to the content of @oldent.
*/
err = ntfs_link(ni, dir_ni, uname, uname_len);
if (err) {
antfs_log_error("Couldn't link new->parent to ino!");
goto free_name;
}
/* - update ni and dir_ni and sync it back to disk - */
ni->flags |= FILE_ATTR_ARCHIVE;
/* - unlink the old entry - */
uname_len = ntfs_mbstoucs(oldent->d_name.name, &old_uname);
if (uname_len < 0) {
err = uname_len;
goto free_name;
}
/* Just unlink. Link counter is still at least 1 after this. */
err = ntfs_unlink(sbi->vol, ni, old_dir_ni, old_uname, uname_len);
if (err) {
antfs_log_error("During unlink of oldent: %d", err);
goto free_names;
}
/* the index_root attribute changed it's data_size, we have to
* pass that to the vfs inode's i_size
*/
i_size_write(newdir, ANTFS_NA(dir_ni)->data_size);
i_size_write(olddir, ANTFS_NA(old_dir_ni)->data_size);
ntfs_inode_mark_dirty(ni);
ntfs_inode_mark_dirty(dir_ni);
if (dir_ni != old_dir_ni)
ntfs_inode_mark_dirty(old_dir_ni);
mark_inode_dirty(oldent->d_inode);
mark_inode_dirty(newdir);
if (newdir != olddir)
mark_inode_dirty(olddir);
err = 0;
free_names:
ntfs_free(old_uname);
free_name:
ntfs_free(uname);
out:
if (old_dir_ni_locked)
mutex_unlock(&old_dir_ni->ni_lock);
if (dir_ni_locked)
mutex_unlock(&dir_ni->ni_lock);
if (ni_locked)
mutex_unlock(&ni->ni_lock);
return err;
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 11, 0)
/**
* @brief fills in a dirent to create a dentry from the information.
*
* @param buf pointer to struct antfs_filler
* @param name unicode string name of entry
* @param name_len length of name
* @param name_type type of the name, see enum FILE_NAME_TYPE_FLAGS
* @param pos position used by ntfs_readdir()
* @param mref position in the mft table of the entry
* @param type type of the entry (directory, symlink, regular file etc.)
*
* @return 0 if we filled in a dirent, -ENOMEM if we were not able too.
*
* antfs_dir_read uses the filldir() function that is provided by the vfs
* to write dirents into a buffer that will be used to create dentries from
* that. filldir() takes a regular char string instead of a uc string as a
* parameter so the name needs to be converted into a regular string. All
* other paramters can be just taken as is to use filldir().
* The antfs_filler struct is used to keep track of the progress of reading
* entries from the disk. It keeps the file, so that the f_pos counter gets
* incremented for every dirent that gets read from the disk. In case of too
* many entries in the directory the provided buffer will be filled before
* done reading the entries. The vfs will call readdir() as long as we will
* fill in more entries.
* For not adding duplicates we need to check if the current entry is already
* filled in, in which case @pos would equal the filler->file->f_pos and is not
* the root entry '.' with @pos = 0. This could mean two things: this is the
* last entry that was added before we run out of memory to fill in more
* entries, or we read all entries already. In both cases we just ignore the
* current entry, ntfs_readdir() will either way call us with more entries,
* or return to the caller antfs_readdir().
*/
static int antfs_dir_read(void *buf, ntfschar *name, int name_len,
int name_type, long long pos, MFT_REF mref, unsigned type)
{
struct antfs_filler *filler = (struct antfs_filler *)buf;
char *filename = NULL;
int err = 0;
/* To emulate the behaviour of the ntfs-3g we don't show DOS file names.
* Files created on Windows have two file name attributes with type DOS
* and WIN32 or one attribute with type DOS_AND_WIN32 if both would be
* the same name.
*/
if (name_type == FILE_NAME_DOS)
goto out;
/* - if we already read this part, just return 0 - */
if (pos < filler->file->f_pos && pos != 0)
goto out;
err = ntfs_ucstombs(name, name_len, &filename, 0);
if (err < 0) {
/* - we have to return an error, so we take out of memory - */
antfs_log_error("Failed to convert ucs to char *");
goto out;
} else {
name_len = err;
err = 0;
}
/* We don't want to show the first 16 MFT entries except root.
* Also we dont wanna blend out '..' which can have any ino, therefore
* we check for a leading '$'
*/
if (MREF(mref) < FILE_FIRST_USER)
if (filename[0] == '$')
goto free;
err = filler->filldir(filler->buffer, filename, name_len,
pos, MREF(mref), type);
if (err) {
/* - if we cant submit more dirent's -> out of memory - */
antfs_log_debug("Out of memory");
goto free;
}
filler->file->f_pos = pos;
free:
ntfs_free(filename);
out:
return err;
}
/**
* @brief reading the entries of an directory.
*
* @param filp filp struct of the directory to read
* @param dstbuf dirent buffer vfs wants to be filled with entries
* @param filldir function that has to be used to fill in the dstbuf
*
* @return 0 if successfully read the directory in question
*
* antfs_readdir is called by the vfs to eventually provide the filldir()
* function with the right parameters to fill in the given buffer. With the
* use of ntfs_readdir which is using a given function to fill in dirent's
* a couple problems occur:
* 1.) ntfs_readdir() is using the provided function with one argument more,
* than the one provided by the vfs filldir() function, so we can't use
* that one straight away.
* 2.) ntfs uses unicode strings for saving file names, instead of regular
* char strings like the vfs wants them. In addition all dirents are gonna
* read at once without knowing beforehand how many entries there will be.
* To solve these issues we take another function to wrap around filldir().
* antfs_dir_read() is used to convert the uc strings into regular strings
* and use filldir() to fill in the buffer the vfs provided. To pass along
* filldir() and dstbuf to antfs_dir_read() a antfs_filler struct is used
* which also keeps the file struct for a check if we already read that dirent.
*/
static int antfs_readdir(struct file *filp, void *dstbuf, filldir_t filldir)
{
struct antfs_filler filler;
struct ntfs_inode *ni = ANTFS_NI(file_inode(filp));
long long pos = filp->f_pos;
int err;
filler.filldir = filldir;
filler.buffer = dstbuf;
filler.file = filp;
if (mutex_lock_interruptible_nested(&ni->ni_lock, NI_MUTEX_NORMAL))
return -ERESTARTSYS;
err = ntfs_readdir(ni, &pos, &filler, (ntfs_filldir_t) antfs_dir_read);
mutex_unlock(&ni->ni_lock);
filp->f_pos = (loff_t) pos;
return err;
}
#else
/**
* @brief fills in a dirent to create a dentry from the information.
*
* @param buf pointer to struct antfs_filler
* @param name unicode string name of entry
* @param name_len length of name
* @param name_type type of the name, see enum FILE_NAME_TYPE_FLAGS
* @param pos position used by ntfs_readdir() unused
* @param mref position in the mft table of the entry
* @param type type of the entry (directory, symlink, regular file etc.)
*
* @return 0 if we filled in a dirent, -ENOMEM if we were not able too.
*
* antfs_dir_iterate uses the @ctx->actor() function that is provided by the
* vfs to write dirents into a buffer that will be used to create dentries from
* that. @ctx->actor() takes a regular char string instead of a uc string as a
* parameter so the name needs to be converted into a regular string. All
* other paramters can be just taken as is to use @ctx->actor().
* The antfs_filler struct is used to keep track of the progress of reading
* entries from the disk. The ctx->pos counter gets incremented for every dirent
* that gets read from the disk. In case of too many entries in the directory
* the provided buffer will be filled before done reading the entries. The vfs
* will call readdir() as long as we will fill in more entries.
* For not adding duplicates we need to check if the current entry is already
* filled in, in which case @pos would equal the filler->ctx->pos and is not
* the root entry '.' with @pos = 0. This could mean two things: this is the
* last entry that was added before we run out of memory to fill in more
* entries, or we read all entries already. In both cases we just ignore the
* current entry, ntfs_readdir() will either way call us with more entries,
* or return to the caller antfs_iterate().
*
* This is also used as filldir function in e.g. @ref ntfs_readdir
*/
static int antfs_dir_iterate(void *buf, ntfschar *name, int name_len,
int name_type, long long pos,
MFT_REF mref, unsigned type)
{
struct antfs_filler *filler = (struct antfs_filler *)buf;
char *filename = NULL;
int err = 0;
/* To emulate the behaviour of the ntfs-3g we don't show DOS file names.
* Files created on Windows have two file name attributes with type DOS
* and WIN32 or one attribute with type DOS_AND_WIN32 if both would be
* the same name.
*/
if (name_type == FILE_NAME_DOS)
goto out;
/* - if we already read this part, just return 0 - */
if (pos < filler->ctx->pos && pos != 0)
goto out;
err = ntfs_ucstombs(name, name_len, &filename, 0);
if (err < 0) {
/* - we have to return an error, so we take out of memory - */
antfs_log_error("Failed to convert ucs to char *");
goto out;
} else {
name_len = err;
err = 0;
}
/* We don't want to show the first 16 MFT entries except root.
* Also we dont wanna blend out '..' which can have any ino, therefore
* we check for a leading '$'
*/
if (MREF(mref) < FILE_FIRST_USER)
if (filename[0] == '$')
goto free;
antfs_log_debug("dirent: %s | ino: %llu | type: %x",
filename, MREF(mref), type);
err = !dir_emit(filler->ctx, filename, name_len, MREF(mref), type);
if (err) {
/* If we can't submit more dirent's -> out of memory;
* This is not really an error. */
antfs_log_info("out of Memory");
err = -ENOMEM;
goto free;
}
filler->ctx->pos = pos;
free:
ntfs_free(filename);
out:
antfs_log_leave("Exit (%d)", err);
return err;
}