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//
47struct doc_tombstone *
48doc_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//
68void
69doc_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(vp: 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(vp: 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(vp: 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".
113bool
114doc_tombstone_should_ignore_name(const char *nameptr, int len)
115{
116 size_t real_len;
117 if (len == 0) {
118 real_len = strlen(s: nameptr);
119 } else {
120 real_len = (size_t)len;
121 }
122
123 if (strncmp(s1: nameptr, s2: "atmp", n: 4) == 0
124 || (real_len > 4 && strncmp(s1: nameptr + real_len - 4, s2: ".bak", n: 4) == 0)
125 || (real_len > 4 && strncmp(s1: nameptr + real_len - 4, s2: ".tmp", n: 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//
137bool
138doc_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(nameptr: cnp->cn_nameptr, len: 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//
166void
167doc_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(vp: 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(dst: (char *)&ut->t_lastop_filename[0], src: cnp->cn_nameptr, n: sizeof(ut->t_lastop_filename));
182}
183