source: trunk/server/source3/modules/onefs_notify.c@ 845

Last change on this file since 845 was 745, checked in by Silvan Scherrer, 13 years ago

Samba Server: updated trunk to 3.6.0

File size: 22.2 KB
Line 
1/*
2 * Unix SMB/CIFS implementation.
3 *
4 * Support for change notify using OneFS's file event notification system
5 *
6 * Copyright (C) Andrew Tridgell, 2006
7 * Copyright (C) Steven Danneman, 2008
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 3 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, see <http://www.gnu.org/licenses/>.
21 */
22
23/* Implement handling of change notify requests on files and directories using
24 * Isilon OneFS's "ifs event" file notification system.
25 *
26 * The structure of this file is based off the implementation of change notify
27 * using the inotify system calls in smbd/notify_inotify.c */
28
29/* TODO: We could reduce the number of file descriptors used by merging
30 * multiple watch requests on the same directory into the same
31 * onefs_notify_watch_context. To do this we'd need mux/demux routines that
32 * when receiving an event on that watch context would check it against the
33 * CompletionFilter and WatchTree of open SMB requests, and return notify
34 * events back to the proper SMB requests */
35
36#include "includes.h"
37#include "smbd/smbd.h"
38#include "onefs.h"
39
40#include <ifs/ifs_types.h>
41#include <ifs/ifs_syscalls.h>
42#include <isi_util/syscalls.h>
43
44#include <sys/event.h>
45
46#define ONEFS_IFS_EVENT_MAX_NUM 8
47#define ONEFS_IFS_EVENT_MAX_BYTES (ONEFS_IFS_EVENT_MAX_NUM * \
48 sizeof(struct ifs_event))
49
50struct onefs_notify_watch_context {
51 struct sys_notify_context *ctx;
52 int watch_fd;
53 ino_t watch_lin;
54 const char *path;
55 int ifs_event_fd;
56 uint32_t ifs_filter; /* the ifs event mask */
57 uint32_t smb_filter; /* the windows completion filter */
58 void (*callback)(struct sys_notify_context *ctx,
59 void *private_data,
60 struct notify_event *ev);
61 void *private_data;
62};
63
64/**
65 * Conversion map from a SMB completion filter to an IFS event mask.
66 */
67static const struct {
68 uint32_t smb_filter;
69 uint32_t ifs_filter;
70} onefs_notify_conv[] = {
71 {FILE_NOTIFY_CHANGE_FILE_NAME,
72 NOTE_CREATE | NOTE_DELETE | NOTE_RENAME_FROM | NOTE_RENAME_TO},
73 {FILE_NOTIFY_CHANGE_DIR_NAME,
74 NOTE_CREATE | NOTE_DELETE | NOTE_RENAME_FROM | NOTE_RENAME_TO},
75 {FILE_NOTIFY_CHANGE_ATTRIBUTES,
76 NOTE_CREATE | NOTE_DELETE | NOTE_RENAME_FROM | NOTE_RENAME_TO |
77 NOTE_ATTRIB},
78 {FILE_NOTIFY_CHANGE_SIZE,
79 NOTE_SIZE | NOTE_EXTEND},
80 {FILE_NOTIFY_CHANGE_LAST_WRITE,
81 NOTE_WRITE | NOTE_ATTRIB},
82 /* OneFS doesn't set atime by default, but we can somewhat fake it by
83 * notifying for other events that imply ACCESS */
84 {FILE_NOTIFY_CHANGE_LAST_ACCESS,
85 NOTE_WRITE | NOTE_ATTRIB},
86 /* We don't have an ifs_event for the setting of create time, but we
87 * can fake by notifying when a "new" file is created via rename */
88 {FILE_NOTIFY_CHANGE_CREATION,
89 NOTE_RENAME_TO},
90 {FILE_NOTIFY_CHANGE_SECURITY,
91 NOTE_SECURITY},
92 /* Unsupported bits
93 FILE_NOTIFY_CHANGE_EA (no EAs in OneFS)
94 FILE_NOTIFY_CHANGE_STREAM_NAME (no ifs_event equivalent)
95 FILE_NOTIFY_CHANGE_STREAM_SIZE (no ifs_event equivalent)
96 FILE_NOTIFY_CHANGE_STREAM_WRITE (no ifs_event equivalent) */
97};
98
99#define ONEFS_NOTIFY_UNSUPPORTED (FILE_NOTIFY_CHANGE_LAST_ACCESS | \
100 FILE_NOTIFY_CHANGE_CREATION | \
101 FILE_NOTIFY_CHANGE_EA | \
102 FILE_NOTIFY_CHANGE_STREAM_NAME | \
103 FILE_NOTIFY_CHANGE_STREAM_SIZE | \
104 FILE_NOTIFY_CHANGE_STREAM_WRITE)
105
106/**
107 * Convert Windows/SMB filter/flags to IFS event filter.
108 *
109 * @param[in] smb_filter Windows Completion Filter sent in the SMB
110 *
111 * @return ifs event filter mask
112 */
113static uint32_t
114onefs_notify_smb_filter_to_ifs_filter(uint32_t smb_filter)
115{
116 int i;
117 uint32_t ifs_filter = 0x0;
118
119 for (i=0;i<ARRAY_SIZE(onefs_notify_conv);i++) {
120 if (onefs_notify_conv[i].smb_filter & smb_filter) {
121 ifs_filter |= onefs_notify_conv[i].ifs_filter;
122 }
123 }
124
125 return ifs_filter;
126}
127
128/**
129 * Convert IFS filter/flags to a Windows notify action.
130 *
131 * Returns Win notification actions, types (1-5).
132 *
133 * @param[in] smb_filter Windows Completion Filter sent in the SMB
134 * @param[in] ifs_filter Returned from the kernel in the ifs_event
135 *
136 * @return 0 if there are no more relevant flags.
137 */
138static int
139onefs_notify_ifs_filter_to_smb_action(uint32_t smb_filter, uint32_t ifs_filter)
140{
141 /* Handle Windows special cases, before modifying events bitmask */
142
143 /* Special case 1: win32api->MoveFile needs to send a modified
144 * notification on the new file, if smb_filter == ATTRIBUTES.
145 * Here we handle the case where two separate ATTR & NAME notifications
146 * have been registered. We handle the case where both bits are set in
147 * the same registration in onefs_notify_dispatch() */
148 if ((smb_filter & FILE_NOTIFY_CHANGE_ATTRIBUTES) &&
149 !(smb_filter & FILE_NOTIFY_CHANGE_FILE_NAME) &&
150 (ifs_filter & NOTE_FILE) && (ifs_filter & NOTE_RENAME_TO))
151 {
152 return NOTIFY_ACTION_MODIFIED;
153 }
154
155 /* Special case 2: Writes need to send a modified
156 * notification on the file, if smb_filter = ATTRIBUTES. */
157 if ((smb_filter & FILE_NOTIFY_CHANGE_ATTRIBUTES) &&
158 (ifs_filter & NOTE_FILE) && (ifs_filter & NOTE_WRITE))
159 {
160 return NOTIFY_ACTION_MODIFIED;
161 }
162
163 /* Loop because some events may be filtered out. Eventually all
164 * relevant events will be taken care of and returned. */
165 while (1) {
166 if (ifs_filter & NOTE_CREATE) {
167 ifs_filter &= ~NOTE_CREATE;
168 if ((smb_filter & FILE_NOTIFY_CHANGE_FILE_NAME) &&
169 (ifs_filter & NOTE_FILE))
170 return NOTIFY_ACTION_ADDED;
171 if ((smb_filter & FILE_NOTIFY_CHANGE_DIR_NAME) &&
172 (ifs_filter & NOTE_DIRECTORY))
173 return NOTIFY_ACTION_ADDED;
174 }
175 else if (ifs_filter & NOTE_DELETE) {
176 ifs_filter &= ~NOTE_DELETE;
177 if ((smb_filter & FILE_NOTIFY_CHANGE_FILE_NAME) &&
178 (ifs_filter & NOTE_FILE))
179 return NOTIFY_ACTION_REMOVED;
180 if ((smb_filter & FILE_NOTIFY_CHANGE_DIR_NAME) &&
181 (ifs_filter & NOTE_DIRECTORY))
182 return NOTIFY_ACTION_REMOVED;
183 }
184 else if (ifs_filter & NOTE_WRITE) {
185 ifs_filter &= ~NOTE_WRITE;
186 if ((smb_filter & FILE_NOTIFY_CHANGE_LAST_WRITE) ||
187 (smb_filter & FILE_NOTIFY_CHANGE_LAST_ACCESS))
188 return NOTIFY_ACTION_MODIFIED;
189 }
190 else if ((ifs_filter & NOTE_SIZE) || (ifs_filter & NOTE_EXTEND)) {
191 ifs_filter &= ~NOTE_SIZE;
192 ifs_filter &= ~NOTE_EXTEND;
193
194 /* TODO: Do something on NOTE_DIR? */
195 if ((smb_filter & FILE_NOTIFY_CHANGE_SIZE) &&
196 (ifs_filter & NOTE_FILE))
197 return NOTIFY_ACTION_MODIFIED;
198 }
199 else if (ifs_filter & NOTE_ATTRIB) {
200 ifs_filter &= ~NOTE_ATTRIB;
201 /* NOTE_ATTRIB needs to be converted to a
202 * LAST_WRITE as well, because we need to send
203 * LAST_WRITE when the mtime changes. Looking into
204 * better alternatives as this causes extra LAST_WRITE
205 * notifications. We also return LAST_ACCESS as a
206 * modification to attribs implies this. */
207 if ((smb_filter & FILE_NOTIFY_CHANGE_ATTRIBUTES) ||
208 (smb_filter & FILE_NOTIFY_CHANGE_LAST_WRITE) ||
209 (smb_filter & FILE_NOTIFY_CHANGE_LAST_ACCESS))
210 return NOTIFY_ACTION_MODIFIED;
211 }
212 else if (ifs_filter & NOTE_LINK) {
213 ifs_filter &= ~NOTE_LINK;
214 /* NOTE_LINK will send out NO notifications */
215 }
216 else if (ifs_filter & NOTE_REVOKE) {
217 ifs_filter &= ~NOTE_REVOKE;
218 /* NOTE_REVOKE will send out NO notifications */
219 }
220 else if (ifs_filter & NOTE_RENAME_FROM) {
221 ifs_filter &= ~NOTE_RENAME_FROM;
222
223 if (((smb_filter & FILE_NOTIFY_CHANGE_FILE_NAME) &&
224 (ifs_filter & NOTE_FILE)) ||
225 ((smb_filter & FILE_NOTIFY_CHANGE_DIR_NAME) &&
226 (ifs_filter & NOTE_DIRECTORY))) {
227 /* Check if this is a RENAME, not a MOVE */
228 if (ifs_filter & NOTE_RENAME_SAMEDIR) {
229 /* Remove the NOTE_RENAME_SAMEDIR, IFF
230 * RENAME_TO is not in this event */
231 if (!(ifs_filter & NOTE_RENAME_TO))
232 ifs_filter &=