00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "includes.h"
00023
00024 #include <fam.h>
00025
00026 #if !defined(HAVE_FAM_H_FAMCODES_TYPEDEF)
00027
00028
00029
00030 typedef enum FAMCodes FAMCodes;
00031 #endif
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047 struct fam_watch_context {
00048 struct fam_watch_context *prev, *next;
00049 FAMConnection *fam_connection;
00050 struct FAMRequest fr;
00051 struct sys_notify_context *sys_ctx;
00052 void (*callback)(struct sys_notify_context *ctx,
00053 void *private_data,
00054 struct notify_event *ev);
00055 void *private_data;
00056 uint32_t mask;
00057 uint32_t filter;
00058 const char *path;
00059 };
00060
00061
00062
00063
00064
00065 static FAMConnection fam_connection;
00066 static BOOL fam_connection_initialized = False;
00067
00068 static struct fam_watch_context *fam_notify_list;
00069 static void fam_handler(struct event_context *event_ctx,
00070 struct fd_event *fd_event,
00071 uint16 flags,
00072 void *private_data);
00073
00074 static NTSTATUS fam_open_connection(FAMConnection *fam_conn,
00075 struct event_context *event_ctx)
00076 {
00077 int res;
00078 char *name;
00079
00080 ZERO_STRUCTP(fam_conn);
00081 FAMCONNECTION_GETFD(fam_conn) = -1;
00082
00083 if (asprintf(&name, "smbd (%lu)", (unsigned long)sys_getpid()) == -1) {
00084 DEBUG(0, ("No memory\n"));
00085 return NT_STATUS_NO_MEMORY;
00086 }
00087
00088 res = FAMOpen2(fam_conn, name);
00089 SAFE_FREE(name);
00090
00091 if (res < 0) {
00092 DEBUG(10, ("FAM file change notifications not available\n"));
00093
00094
00095
00096 FAMCONNECTION_GETFD(fam_conn) = -1;
00097 return NT_STATUS_UNEXPECTED_IO_ERROR;
00098 }
00099
00100 if (event_add_fd(event_ctx, event_ctx,
00101 FAMCONNECTION_GETFD(fam_conn),
00102 EVENT_FD_READ, fam_handler,
00103 (void *)fam_conn) == NULL) {
00104 DEBUG(0, ("event_add_fd failed\n"));
00105 FAMClose(fam_conn);
00106 FAMCONNECTION_GETFD(fam_conn) = -1;
00107 return NT_STATUS_NO_MEMORY;
00108 }
00109
00110 return NT_STATUS_OK;
00111 }
00112
00113 static void fam_reopen(FAMConnection *fam_conn,
00114 struct event_context *event_ctx,
00115 struct fam_watch_context *notify_list)
00116 {
00117 struct fam_watch_context *ctx;
00118
00119 DEBUG(5, ("Re-opening FAM connection\n"));
00120
00121 FAMClose(fam_conn);
00122
00123 if (!NT_STATUS_IS_OK(fam_open_connection(fam_conn, event_ctx))) {
00124 DEBUG(5, ("Re-opening fam connection failed\n"));
00125 return;
00126 }
00127
00128 for (ctx = notify_list; ctx; ctx = ctx->next) {
00129 FAMMonitorDirectory(fam_conn, ctx->path, &ctx->fr, NULL);
00130 }
00131 }
00132
00133 static void fam_handler(struct event_context *event_ctx,
00134 struct fd_event *fd_event,
00135 uint16 flags,
00136 void *private_data)
00137 {
00138 FAMConnection *fam_conn = (FAMConnection *)private_data;
00139 FAMEvent fam_event;
00140 struct fam_watch_context *ctx;
00141 struct notify_event ne;
00142
00143 if (FAMPending(fam_conn) == 0) {
00144 DEBUG(10, ("fam_handler called but nothing pending\n"));
00145 return;
00146 }
00147
00148 if (FAMNextEvent(fam_conn, &fam_event) != 1) {
00149 DEBUG(5, ("FAMNextEvent returned an error\n"));
00150 TALLOC_FREE(fd_event);
00151 fam_reopen(fam_conn, event_ctx, fam_notify_list);
00152 return;
00153 }
00154
00155 DEBUG(10, ("Got FAMCode %d for %s\n", fam_event.code,
00156 fam_event.filename));
00157
00158 switch (fam_event.code) {
00159 case FAMChanged:
00160 ne.action = NOTIFY_ACTION_MODIFIED;
00161 break;
00162 case FAMCreated:
00163 ne.action = NOTIFY_ACTION_ADDED;
00164 break;
00165 case FAMDeleted:
00166 ne.action = NOTIFY_ACTION_REMOVED;
00167 break;
00168 default:
00169 DEBUG(10, ("Ignoring code FAMCode %d for file %s\n",
00170 (int)fam_event.code, fam_event.filename));
00171 return;
00172 }
00173
00174 for (ctx = fam_notify_list; ctx; ctx = ctx->next) {
00175 if (memcmp(&fam_event.fr, &ctx->fr, sizeof(FAMRequest)) == 0) {
00176 break;
00177 }
00178 }
00179
00180 if (ctx == NULL) {
00181 DEBUG(5, ("Discarding event for file %s\n",
00182 fam_event.filename));
00183 return;
00184 }
00185
00186 if ((ne.path = strrchr_m(fam_event.filename, '\\')) == NULL) {
00187 ne.path = fam_event.filename;
00188 }
00189
00190 ctx->callback(ctx->sys_ctx, ctx->private_data, &ne);
00191 }
00192
00193 static int fam_watch_context_destructor(struct fam_watch_context *ctx)
00194 {
00195 if (FAMCONNECTION_GETFD(ctx->fam_connection) != -1) {
00196 FAMCancelMonitor(&fam_connection, &ctx->fr);
00197 }
00198 DLIST_REMOVE(fam_notify_list, ctx);
00199 return 0;
00200 }
00201
00202
00203
00204
00205
00206 static NTSTATUS fam_watch(vfs_handle_struct *vfs_handle,
00207 struct sys_notify_context *ctx,
00208 struct notify_entry *e,
00209 void (*callback)(struct sys_notify_context *ctx,
00210 void *private_data,
00211 struct notify_event *ev),
00212 void *private_data,
00213 void *handle_p)
00214 {
00215 const uint32 fam_mask = (FILE_NOTIFY_CHANGE_FILE_NAME|
00216 FILE_NOTIFY_CHANGE_DIR_NAME);
00217 struct fam_watch_context *watch;
00218 void **handle = (void **)handle_p;
00219
00220 if ((e->filter & fam_mask) == 0) {
00221 DEBUG(10, ("filter = %u, ignoring in FAM\n", e->filter));
00222 return NT_STATUS_OK;
00223 }
00224
00225 if (!fam_connection_initialized) {
00226 if (!NT_STATUS_IS_OK(fam_open_connection(&fam_connection,
00227 ctx->ev))) {
00228
00229
00230
00231 return NT_STATUS_OK;
00232 }
00233 fam_connection_initialized = True;
00234 }
00235
00236 if (!(watch = TALLOC_P(ctx, struct fam_watch_context))) {
00237 return NT_STATUS_NO_MEMORY;
00238 }
00239
00240 watch->fam_connection = &fam_connection;
00241
00242 watch->callback = callback;
00243 watch->private_data = private_data;
00244 watch->sys_ctx = ctx;
00245
00246 if (!(watch->path = talloc_strdup(watch, e->path))) {
00247 DEBUG(0, ("talloc_asprintf failed\n"));
00248 TALLOC_FREE(watch);
00249 return NT_STATUS_NO_MEMORY;
00250 }
00251
00252
00253
00254
00255
00256
00257
00258 watch->filter = fam_mask;
00259 e->filter &= ~fam_mask;
00260
00261 DLIST_ADD(fam_notify_list, watch);
00262 talloc_set_destructor(watch, fam_watch_context_destructor);
00263
00264
00265
00266
00267
00268 if (FAMCONNECTION_GETFD(watch->fam_connection) != -1) {
00269 FAMMonitorDirectory(watch->fam_connection, watch->path,
00270 &watch->fr, NULL);
00271 }
00272 else {
00273
00274
00275
00276
00277 fam_reopen(watch->fam_connection, ctx->ev, fam_notify_list);
00278 }
00279
00280 *handle = (void *)watch;
00281
00282 return NT_STATUS_OK;
00283 }
00284
00285
00286
00287 static vfs_op_tuple notify_fam_op_tuples[] = {
00288
00289 {SMB_VFS_OP(fam_watch),
00290 SMB_VFS_OP_NOTIFY_WATCH,
00291 SMB_VFS_LAYER_OPAQUE},
00292
00293 {SMB_VFS_OP(NULL),
00294 SMB_VFS_OP_NOOP,
00295 SMB_VFS_LAYER_NOOP}
00296
00297 };
00298
00299
00300 NTSTATUS vfs_notify_fam_init(void);
00301 NTSTATUS vfs_notify_fam_init(void)
00302 {
00303 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "notify_fam",
00304 notify_fam_op_tuples);
00305 }