Beispiel #1
0
static HANDLE add_watch (SeafWTMonitorPriv *priv,
                         const char *repo_id,
                         const char *worktree)
{
    HANDLE dir_handle = NULL;
    wchar_t *path = NULL;
    RepoWatchInfo *info;

    /* worktree is in utf8, need to convert to wchar in win32 */
    path = wchar_from_utf8 (worktree);

    dir_handle = get_handle_of_path (path);
    if (!dir_handle) {
        seaf_warning ("failed to open handle for worktree "
                      "of repo  %s\n", repo_id);
        g_free (path);
        return NULL;
    }
    g_free (path);

    pthread_mutex_lock (&priv->hash_lock);
    g_hash_table_insert (priv->handle_hash,
                         g_strdup(repo_id), (gpointer)(long)dir_handle);

    info = create_repo_watch_info (repo_id, worktree);
    g_hash_table_insert (priv->info_hash, (gpointer)(long)dir_handle, info);
    pthread_mutex_unlock (&priv->hash_lock);

    add_event_to_queue (info->status, WT_EVENT_CREATE_OR_UPDATE, "", NULL);

    return dir_handle;
}
Beispiel #2
0
/*
 * On Windows, RENAMED_OLD_NAME and RENAMED_NEW_NAME always comes in pairs.
 * If a file or dir is moved in/out of the worktree, ADDED or REMOVED event
 * will be emitted by the kernel.
 * 
 * This is a two-state state machine. The states are 'not processing rename' and
 * 'processing rename'.
 */
static void
handle_rename (RepoWatchInfo *info,
               PFILE_NOTIFY_INFORMATION event,
               const char *worktree,
               const char *filename,
               gboolean last_event)
{
    WTStatus *status = info->status;
    RenameInfo *rename_info = info->rename_info;

    if (event->Action == FILE_ACTION_RENAMED_OLD_NAME)
        seaf_debug ("Move %s ->\n", filename);
    else if (event->Action == FILE_ACTION_RENAMED_NEW_NAME)
        seaf_debug ("Move -> %s.\n", filename);

    if (!rename_info->processing) {
        if (event->Action == FILE_ACTION_RENAMED_OLD_NAME) {
            if (!last_event) {
                set_rename_processing_state (rename_info, filename);
            } else {
                /* RENAMED_OLD_NAME should not be the last event,
                   just ignore it.
                */
            }
        }
    } else {
        if (event->Action == FILE_ACTION_RENAMED_NEW_NAME) {
            /* Rename pair detected. */
            add_event_to_queue (status, WT_EVENT_RENAME,
                                rename_info->old_path, filename);
            unset_rename_processing_state (rename_info);
        }
    }
}
Beispiel #3
0
static void
process_one_event (RepoWatchInfo *info,
                   const char *worktree,
                   PFILE_NOTIFY_INFORMATION event,
                   gboolean last_event)
{
    WTStatus *status = info->status;
    char *filename;
    gboolean add_to_queue = TRUE;

#if 0
    if (handle_consecutive_duplicate_event (info, event))
        add_to_queue = FALSE;
#endif

    filename = convert_to_unix_path (event->FileName, event->FileNameLength);

    handle_rename (info, event, worktree, filename, last_event);

    if (event->Action == FILE_ACTION_MODIFIED) {
        seaf_debug ("Modified %s.\n", filename);

        /* Ignore modified event for directories. */
        char *full_path = g_build_filename (worktree, filename, NULL);
        if (g_file_test(full_path, G_FILE_TEST_IS_DIR)) {
            g_free (full_path);
            goto out;
        }
        g_free (full_path);

        if (add_to_queue)
            add_event_to_queue (status, WT_EVENT_CREATE_OR_UPDATE, filename, NULL);
    } else if (event->Action == FILE_ACTION_ADDED) {
        seaf_debug ("Created %s.\n", filename);
        add_event_to_queue (status, WT_EVENT_CREATE_OR_UPDATE, filename, NULL);
    } else if (event->Action == FILE_ACTION_REMOVED) {
        seaf_debug ("Deleted %s.\n", filename);
        add_event_to_queue (status, WT_EVENT_DELETE, filename, NULL);
    }

out:
    g_free (filename);
    g_atomic_int_set (&info->status->last_changed, (gint)time(NULL));

}
Beispiel #4
0
static FSEventStreamRef
add_watch (SeafWTMonitor *monitor, const char* repo_id, const char* worktree)
{
    SeafWTMonitorPriv *priv = monitor->priv;
    RepoWatchInfo *info;
    double latency = 0.25; /* unit: second */

    char *worktree_nfd = g_utf8_normalize (worktree, -1, G_NORMALIZE_NFD);

    CFStringRef mypaths[1];
    mypaths[0] = CFStringCreateWithCString (kCFAllocatorDefault,
                                            worktree_nfd, kCFStringEncodingUTF8);
    g_free (worktree_nfd);
    CFArrayRef pathsToWatch = CFArrayCreate(NULL, (const void **)mypaths, 1, NULL);
    FSEventStreamRef stream;

    /* Create the stream, passing in a callback */
    seaf_debug("Use kFSEventStreamCreateFlagWatchRoot\n");
    // kFSEventStreamCreateFlagFileEvents does not work for libraries with name
    // containing accent characters.
    struct FSEventStreamContext ctx = {0, monitor, NULL, NULL, NULL};
    stream = FSEventStreamCreate(kCFAllocatorDefault,
                                 stream_callback,
                                 &ctx,
                                 pathsToWatch,
                                 kFSEventStreamEventIdSinceNow,
                                 latency,
                                 kFSEventStreamCreateFlagWatchRoot
                                 );

    CFRelease (mypaths[0]);
    CFRelease (pathsToWatch);

    if (!stream) {
        seaf_warning ("[wt] Failed to create event stream.\n");
        return stream;
    }

    FSEventStreamScheduleWithRunLoop(stream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
    FSEventStreamStart (stream);
    /* FSEventStreamShow (stream); */
    seaf_debug ("[wt mon] Add repo %s watch success: %s.\n", repo_id, worktree);

    pthread_mutex_lock (&priv->hash_lock);
    g_hash_table_insert (priv->handle_hash,
                         g_strdup(repo_id), (gpointer)(long)stream);

    info = create_repo_watch_info (repo_id, worktree);
    g_hash_table_insert (priv->info_hash, (gpointer)(long)stream, info);
    pthread_mutex_unlock (&priv->hash_lock);

    /* An empty path indicates repo-mgr to scan the whole worktree. */
    add_event_to_queue (info->status, WT_EVENT_CREATE_OR_UPDATE, "", NULL);
    return stream;
}
static FSEventStreamRef
add_watch (SeafWTMonitor *monitor, const char* repo_id, const char* worktree)
{
    SeafWTMonitorPriv *priv = monitor->priv;
    const char *path = worktree;
    RepoWatchInfo *info;
    double latency = 0.25; /* unit: second */

    CFStringRef mypath = CFStringCreateWithCString (kCFAllocatorDefault,
                                                    path, kCFStringEncodingUTF8);
    CFArrayRef pathsToWatch = CFArrayCreate(NULL, (const void **)&mypath, 1, NULL);
    FSEventStreamRef stream;

    /* Create the stream, passing in a callback */
    struct FSEventStreamContext ctx = {0, monitor, NULL, NULL, NULL};
    stream = FSEventStreamCreate(kCFAllocatorDefault,
                                 stream_callback,
                                 &ctx,
                                 pathsToWatch,
                                 kFSEventStreamEventIdSinceNow,
                                 latency,
                                 kFSEventStreamCreateFlagFileEvents /* deprecated OSX 10.6 support*/
        );

    CFRelease (mypath);
    CFRelease (pathsToWatch);

    if (!stream) {
        seaf_warning ("[wt] Failed to create event stream \n");
        return stream;
    }

    FSEventStreamScheduleWithRunLoop(stream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
    FSEventStreamStart (stream);
#ifdef FSEVENT_DEBUG
    FSEventStreamShow (stream);
    seaf_debug ("[wt mon] Add repo %s watch success :%s.\n", repo_id, repo->worktree);
#endif

    pthread_mutex_lock (&priv->hash_lock);
    g_hash_table_insert (priv->handle_hash,
                         g_strdup(repo_id), (gpointer)(long)stream);

    info = create_repo_watch_info (repo_id, worktree);
    g_hash_table_insert (priv->info_hash, (gpointer)(long)stream, info);
    pthread_mutex_unlock (&priv->hash_lock);

    /* An empty path indicates repo-mgr to scan the whole worktree. */
    add_event_to_queue (info->status, WT_EVENT_CREATE_OR_UPDATE, "", NULL);
    return stream;
}
static void
process_one_event (const char* eventPath,
                   RepoWatchInfo *info,
                   const char *worktree,
                   const FSEventStreamEventId eventId,
                   const FSEventStreamEventFlags eventFlags)
{
    WTStatus *status = info->status;
    char *filename;
    const char *tmp;
    tmp = eventPath + strlen(worktree);
    if (*tmp == '/')
        tmp++;
    filename = g_strdup(tmp);

    /* Reinterpreted RENAMED as combine of CREATED or DELETED event */
    if (eventFlags & kFSEventStreamEventFlagItemRenamed) {
        seaf_debug ("Rename Event Affected: %s \n", filename);
        struct stat buf;
        if (stat (eventPath, &buf)) {
            /* ret = -1, file is gone */
            add_event_to_queue (status, WT_EVENT_DELETE, filename, NULL);
        } else {
            /* ret = 0, file is here, but rename behaviour is unknown to us */
            add_event_to_queue (status, WT_EVENT_CREATE_OR_UPDATE, filename, NULL);
        }
    } else if (eventFlags & kFSEventStreamEventFlagItemModified) {
        seaf_debug ("Modified %s.\n", filename);
        add_event_to_queue (status, WT_EVENT_CREATE_OR_UPDATE, filename, NULL);
    } else if (eventFlags & kFSEventStreamEventFlagItemCreated) {
        seaf_debug ("Created %s.\n", filename);
         /**
          * no need to rechecking recursively in FSEventStream
          *
          * these flags are useful if necessary:
          * kFSEventStreamEventFlagItemIsFile
          * kFSEventStreamEventFlagItemIsDir
          * kFSEventStreamEventFlagItemIsSymlink
          */
        add_event_to_queue (status, WT_EVENT_CREATE_OR_UPDATE, filename, NULL);
    } else if (eventFlags & kFSEventStreamEventFlagItemRemoved) {
        seaf_debug ("Deleted %s.\n", filename);
        add_event_to_queue (status, WT_EVENT_DELETE, filename, NULL);
    } else if (eventFlags & kFSEventStreamEventFlagItemXattrMod) {
        seaf_debug ("XattrMod %s.\n", filename);
        add_event_to_queue (status, WT_EVENT_ATTRIB, filename, NULL);
    }
    //TODO: kFSEventStreamEventFlagRootChanged and
    //kFSEventStreamCreateFlagWatchRoot
    g_free (filename);
    g_atomic_int_set (&info->status->last_changed, (gint)time(NULL));
}
Beispiel #7
0
/* Every time after a read event is processed, we should call
 * ReadDirectoryChangesW() on the dir handle asynchronously for the IOCP to
 * detect the change of the workthree.
 */
static BOOL
start_watch_dir_change(SeafWTMonitorPriv *priv, HANDLE dir_handle)
{
    if (!dir_handle)
        return FALSE;

    BOOL first_alloc = FALSE;
    DirWatchAux *aux = g_hash_table_lookup (priv->buf_hash, dir_handle);

    /* allocate aux buffer at the first watch, it would be freed if the repo
       is removed
    */
    if (!aux) {
        first_alloc = TRUE;
        aux = g_new0(DirWatchAux, 1);
        init_overlapped(&aux->ol);
    }

    /* The ending W of this function indicates that the info recevied about
       the change would be in Unicode(specifically, the name of the file that
       is changed would be encoded in wide char).
    */
    BOOL ret;
    DWORD code;
    RepoWatchInfo *info;
retry:
    ret = ReadDirectoryChangesW
        (dir_handle,            /* dir handle */
         &aux->buf,              /* buf to hold change info */
         DIR_WATCH_BUFSIZE,     /* buf size */
         TRUE,                  /* watch subtree */
         DIR_WATCH_MASK,        /* notify filter */
         NULL,                  /* bytes returned */
         &aux->ol,              /* pointer to overlapped */
         NULL);                 /* completion routine */

    if (!ret) {
        code = GetLastError();
        seaf_warning("Failed to ReadDirectoryChangesW, "
                     "error code %lu", code);

        if (first_alloc)
            /* if failed at the first watch, free the aux buffer */
            g_free(aux);
        else if (code == ERROR_NOTIFY_ENUM_DIR) {
            /* If buffer overflowed after the last call,
             * add an overflow event and retry watch.
             */
            info = g_hash_table_lookup (priv->info_hash, dir_handle);
            add_event_to_queue (info->status, WT_EVENT_OVERFLOW, NULL, NULL);
            goto retry;
        }
    } else {
        if (first_alloc)
            /* insert the aux buffer into hash table at the first watch */
            g_hash_table_insert (priv->buf_hash,
                                 (gpointer)dir_handle, (gpointer)aux);
    }

    return ret;
}
Beispiel #8
0
static void
process_one_event (const char* eventPath,
                   RepoWatchInfo *info,
                   const char *worktree,
                   const FSEventStreamEventId eventId,
                   const FSEventStreamEventFlags eventFlags)
{
    WTStatus *status = info->status;
    char *filename;
    char *event_path_nfc;
    const char *tmp;

    event_path_nfc = g_utf8_normalize (eventPath, -1, G_NORMALIZE_NFC);

    tmp = event_path_nfc + strlen(worktree);
    if (*tmp == '/')
        tmp++;
    filename = g_strdup(tmp);
    g_free (event_path_nfc);

    /* Reinterpreted RENAMED as combine of CREATED or DELETED event */
    if (eventFlags & kFSEventStreamEventFlagItemRenamed) {
        seaf_debug ("Rename Event Affected: %s \n", filename);
        struct stat buf;
        if (stat (eventPath, &buf)) {
            /* ret = -1, file is gone */
            add_event_to_queue (status, WT_EVENT_DELETE, filename, NULL);
        } else {
            /* ret = 0, file is here, but rename behaviour is unknown to us */
            add_event_to_queue (status, WT_EVENT_CREATE_OR_UPDATE, filename, NULL);
        }
    } else if (eventFlags & kFSEventStreamEventFlagItemModified) {
        seaf_debug ("Modified %s.\n", filename);
        add_event_to_queue (status, WT_EVENT_CREATE_OR_UPDATE, filename, NULL);
    } else if (eventFlags & kFSEventStreamEventFlagItemCreated) {
        seaf_debug ("Created %s.\n", filename);
         /**
          * no need to rechecking recursively in FSEventStream
          *
          * these flags are useful if necessary:
          * kFSEventStreamEventFlagItemIsFile
          * kFSEventStreamEventFlagItemIsDir
          * kFSEventStreamEventFlagItemIsSymlink
          */
        add_event_to_queue (status, WT_EVENT_CREATE_OR_UPDATE, filename, NULL);
    } else if (eventFlags & kFSEventStreamEventFlagItemRemoved) {
        seaf_debug ("Deleted %s.\n", filename);
        add_event_to_queue (status, WT_EVENT_DELETE, filename, NULL);
    } else if (eventFlags & kFSEventStreamEventFlagItemXattrMod) {
        seaf_debug ("XattrMod %s.\n", filename);
        add_event_to_queue (status, WT_EVENT_ATTRIB, filename, NULL);
    } else if (eventFlags & kFSEventStreamEventFlagRootChanged) {
        /* An empty path indicates repo-mgr to scan the whole worktree. */
        seaf_debug ("RootChange event.\n");
        add_event_to_queue (info->status, WT_EVENT_CREATE_OR_UPDATE, "", NULL);
    } else {
        seaf_debug ("Unhandled event with flags %x.\n", eventFlags);
    }

    g_free (filename);
    g_atomic_int_set (&info->status->last_changed, (gint)time(NULL));
}