xref: /xnu-10002.61.3/bsd/vfs/doc_tombstone.c (revision 0f4c859e951fba394238ab619495c4e1d54d0f34)
1 /*
2  * Copyright (c) 2015 Apple Inc. All rights reserved.
3  *
4  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5  *
6  * This file contains Original Code and/or Modifications of Original Code
7  * as defined in and that are subject to the Apple Public Source License
8  * Version 2.0 (the 'License'). You may not use this file except in
9  * compliance with the License. The rights granted to you under the License
10  * may not be used to create, or enable the creation or redistribution of,
11  * unlawful or unlicensed copies of an Apple operating system, or to
12  * circumvent, violate, or enable the circumvention or violation of, any
13  * terms of an Apple operating system software license agreement.
14  *
15  * Please obtain a copy of the License at
16  * http://www.opensource.apple.com/apsl/ and read it before using this file.
17  *
18  * The Original Code and all software distributed under the License are
19  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23  * Please see the License for the specific language governing rights and
24  * limitations under the License.
25  *
26  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27  */
28 
29 // -- Document ID Tombstone Support --
30 
31 #include <stdint.h>
32 #include <sys/resource.h>
33 #include <sys/signal.h>
34 #include <sys/vfs_context.h>
35 #include <sys/doc_tombstone.h>
36 #include <sys/vnode_internal.h>
37 #include <sys/fsevents.h>
38 #include <kern/thread.h>
39 #include <kern/kalloc.h>
40 #include <string.h>
41 
42 //
43 // This function gets the doc_tombstone structure for the
44 // current thread.  If the thread doesn't have one, the
45 // structure is allocated.
46 //
47 struct doc_tombstone *
doc_tombstone_get(void)48 doc_tombstone_get(void)
49 {
50 	struct  uthread *ut;
51 	ut = current_uthread();
52 
53 	if (ut->t_tombstone == NULL) {
54 		ut->t_tombstone = kalloc_type(struct doc_tombstone, Z_WAITOK | Z_ZERO);
55 	}
56 
57 	return ut->t_tombstone;
58 }
59 
60 //
61 // This routine clears out the current tombstone for the
62 // current thread and if necessary passes the doc-id of
63 // the tombstone on to the dst_cnode.
64 //
65 // The caller is responsible for generating the appropriate
66 // fsevents.
67 //
68 void
doc_tombstone_clear(struct doc_tombstone * ut,vnode_t * old_vpp)69 doc_tombstone_clear(struct doc_tombstone *ut, vnode_t *old_vpp)
70 {
71 	uint64_t old_id = ut->t_lastop_document_id;
72 
73 	ut->t_lastop_document_id = 0;
74 	ut->t_lastop_parent = NULL;
75 	ut->t_lastop_parent_vid = 0;
76 	ut->t_lastop_filename[0] = '\0';
77 
78 	//
79 	// If the lastop item is still the same and needs to be cleared,
80 	// clear it.  The following isn't ideal because the vnode might
81 	// have been recycled.
82 	//
83 	if (old_vpp) {
84 		*old_vpp = NULL;
85 		if (old_id && ut->t_lastop_item
86 		    && vnode_vid(ut->t_lastop_item) == ut->t_lastop_item_vid) {
87 			int res = vnode_get(ut->t_lastop_item);
88 			if (!res) {
89 				// Need to check vid again
90 				if (vnode_vid(ut->t_lastop_item) == ut->t_lastop_item_vid
91 				    && !ISSET(ut->t_lastop_item->v_lflag, VL_TERMINATE)) {
92 					*old_vpp = ut->t_lastop_item;
93 				} else {
94 					vnode_put(ut->t_lastop_item);
95 				}
96 			}
97 		}
98 	}
99 
100 	// last, clear these now that we're all done
101 	ut->t_lastop_item     = NULL;
102 	ut->t_lastop_fileid   = 0;
103 	ut->t_lastop_item_vid = 0;
104 }
105 
106 
107 //
108 // This function is used to filter out operations on temp
109 // filenames.  We have to filter out operations on certain
110 // temp filenames to work-around questionable application
111 // behavior from apps like Autocad that perform unusual
112 // sequences of file system operations for a "safe save".
113 bool
doc_tombstone_should_ignore_name(const char * nameptr,int len)114 doc_tombstone_should_ignore_name(const char *nameptr, int len)
115 {
116 	size_t real_len;
117 	if (len == 0) {
118 		real_len = strlen(nameptr);
119 	} else {
120 		real_len = (size_t)len;
121 	}
122 
123 	if (strncmp(nameptr, "atmp", 4) == 0
124 	    || (real_len > 4 && strncmp(nameptr + real_len - 4, ".bak", 4) == 0)
125 	    || (real_len > 4 && strncmp(nameptr + real_len - 4, ".tmp", 4) == 0)) {
126 		return true;
127 	}
128 
129 	return false;
130 }
131 
132 //
133 // Decide if we need to save a tombstone or not.  Normally we always
134 // save a tombstone - but if there already is one and the name we're
135 // given is an ignorable name, then we will not save a tombstone.
136 //
137 bool
doc_tombstone_should_save(struct doc_tombstone * ut,struct vnode * vp,struct componentname * cnp)138 doc_tombstone_should_save(struct doc_tombstone *ut, struct vnode *vp,
139     struct componentname *cnp)
140 {
141 	if (cnp->cn_nameptr == NULL) {
142 		return false;
143 	}
144 
145 	if (ut->t_lastop_document_id && ut->t_lastop_item == vp
146 	    && doc_tombstone_should_ignore_name(cnp->cn_nameptr, cnp->cn_namelen)) {
147 		return false;
148 	}
149 
150 	return true;
151 }
152 
153 //
154 // This function saves a tombstone for the given vnode and name.  The
155 // tombstone represents the parent directory and name where the document
156 // used to live and the document-id of that file.  This info is recorded
157 // in the doc_tombstone structure hanging off the uthread (which assumes
158 // that all safe-save operations happen on the same thread).
159 //
160 // If later on the same parent/name combo comes back into existence then
161 // we'll preserve the doc-id from this vnode onto the new vnode.
162 //
163 // The caller is responsible for generating the appropriate
164 // fsevents.
165 //
166 void
doc_tombstone_save(struct vnode * dvp,struct vnode * vp,struct componentname * cnp,uint64_t doc_id,ino64_t file_id)167 doc_tombstone_save(struct vnode *dvp, struct vnode *vp,
168     struct componentname *cnp, uint64_t doc_id,
169     ino64_t file_id)
170 {
171 	struct  doc_tombstone *ut;
172 	ut = doc_tombstone_get();
173 
174 	ut->t_lastop_parent         = dvp;
175 	ut->t_lastop_parent_vid     = vnode_vid(dvp);
176 	ut->t_lastop_fileid         = file_id;
177 	ut->t_lastop_item           = vp;
178 	ut->t_lastop_item_vid       = vp ? vnode_vid(vp) : 0;
179 	ut->t_lastop_document_id    = doc_id;
180 
181 	strlcpy((char *)&ut->t_lastop_filename[0], cnp->cn_nameptr, sizeof(ut->t_lastop_filename));
182 }
183