/* * Copyright (c) 2015 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. The rights granted to you under the License * may not be used to create, or enable the creation or redistribution of, * unlawful or unlicensed copies of an Apple operating system, or to * circumvent, violate, or enable the circumvention or violation of, any * terms of an Apple operating system software license agreement. * * Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ // -- Document ID Tombstone Support -- #include #include #include #include #include #include #include #include #include #include // // This function gets the doc_tombstone structure for the // current thread. If the thread doesn't have one, the // structure is allocated. // struct doc_tombstone * doc_tombstone_get(void) { struct uthread *ut; ut = current_uthread(); if (ut->t_tombstone == NULL) { ut->t_tombstone = kalloc_type(struct doc_tombstone, Z_WAITOK | Z_ZERO); } return ut->t_tombstone; } // // This routine clears out the current tombstone for the // current thread and if necessary passes the doc-id of // the tombstone on to the dst_cnode. // // The caller is responsible for generating the appropriate // fsevents. // void doc_tombstone_clear(struct doc_tombstone *ut, vnode_t *old_vpp) { uint64_t old_id = ut->t_lastop_document_id; ut->t_lastop_document_id = 0; ut->t_lastop_parent = NULL; ut->t_lastop_parent_vid = 0; ut->t_lastop_filename[0] = '\0'; // // If the lastop item is still the same and needs to be cleared, // clear it. The following isn't ideal because the vnode might // have been recycled. // if (old_vpp) { *old_vpp = NULL; if (old_id && ut->t_lastop_item && vnode_vid(ut->t_lastop_item) == ut->t_lastop_item_vid) { int res = vnode_get(ut->t_lastop_item); if (!res) { // Need to check vid again if (vnode_vid(ut->t_lastop_item) == ut->t_lastop_item_vid && !ISSET(ut->t_lastop_item->v_lflag, VL_TERMINATE)) { *old_vpp = ut->t_lastop_item; } else { vnode_put(ut->t_lastop_item); } } } } // last, clear these now that we're all done ut->t_lastop_item = NULL; ut->t_lastop_fileid = 0; ut->t_lastop_item_vid = 0; } // // This function is used to filter out operations on temp // filenames. We have to filter out operations on certain // temp filenames to work-around questionable application // behavior from apps like Autocad that perform unusual // sequences of file system operations for a "safe save". bool doc_tombstone_should_ignore_name(const char *nameptr, int len) { size_t real_len; if (len == 0) { real_len = strlen(nameptr); } else { real_len = (size_t)len; } if (strncmp(nameptr, "atmp", 4) == 0 || (real_len > 4 && strncmp(nameptr + real_len - 4, ".bak", 4) == 0) || (real_len > 4 && strncmp(nameptr + real_len - 4, ".tmp", 4) == 0)) { return true; } return false; } // // Decide if we need to save a tombstone or not. Normally we always // save a tombstone - but if there already is one and the name we're // given is an ignorable name, then we will not save a tombstone. // bool doc_tombstone_should_save(struct doc_tombstone *ut, struct vnode *vp, struct componentname *cnp) { if (cnp->cn_nameptr == NULL) { return false; } if (ut->t_lastop_document_id && ut->t_lastop_item == vp && doc_tombstone_should_ignore_name(cnp->cn_nameptr, cnp->cn_namelen)) { return false; } return true; } // // This function saves a tombstone for the given vnode and name. The // tombstone represents the parent directory and name where the document // used to live and the document-id of that file. This info is recorded // in the doc_tombstone structure hanging off the uthread (which assumes // that all safe-save operations happen on the same thread). // // If later on the same parent/name combo comes back into existence then // we'll preserve the doc-id from this vnode onto the new vnode. // // The caller is responsible for generating the appropriate // fsevents. // void doc_tombstone_save(struct vnode *dvp, struct vnode *vp, struct componentname *cnp, uint64_t doc_id, ino64_t file_id) { struct doc_tombstone *ut; ut = doc_tombstone_get(); ut->t_lastop_parent = dvp; ut->t_lastop_parent_vid = vnode_vid(dvp); ut->t_lastop_fileid = file_id; ut->t_lastop_item = vp; ut->t_lastop_item_vid = vp ? vnode_vid(vp) : 0; ut->t_lastop_document_id = doc_id; strlcpy((char *)&ut->t_lastop_filename[0], cnp->cn_nameptr, sizeof(ut->t_lastop_filename)); }