+static int
+fill_buff(uint16_t type, int32_t size, const void *data,
+ char *buff, int32_t *_buff_idx, int32_t buff_sz,
+ struct uio *uio)
+{
+ int32_t amt, error = 0, buff_idx = *_buff_idx;
+ uint16_t tmp;
+
+ //
+ // the +1 on the size is to guarantee that the main data
+ // copy loop will always copy at least 1 byte
+ //
+ if ((buff_sz - buff_idx) <= (int)(2*sizeof(uint16_t) + 1)) {
+ if (buff_idx > uio_resid(uio)) {
+ error = ENOSPC;
+ goto get_out;
+ }
+
+ error = uiomove(buff, buff_idx, uio);
+ if (error) {
+ goto get_out;
+ }
+ buff_idx = 0;
+ }
+
+ // copy out the header (type & size)
+ memcpy(&buff[buff_idx], &type, sizeof(uint16_t));
+ buff_idx += sizeof(uint16_t);
+
+ tmp = size & 0xffff;
+ memcpy(&buff[buff_idx], &tmp, sizeof(uint16_t));
+ buff_idx += sizeof(uint16_t);
+
+ // now copy the body of the data, flushing along the way
+ // if the buffer fills up.
+ //
+ while(size > 0) {
+ amt = (size < (buff_sz - buff_idx)) ? size : (buff_sz - buff_idx);
+ memcpy(&buff[buff_idx], data, amt);
+
+ size -= amt;
+ buff_idx += amt;
+ data = (const char *)data + amt;
+ if (size > (buff_sz - buff_idx)) {
+ if (buff_idx > uio_resid(uio)) {
+ error = ENOSPC;
+ goto get_out;
+ }
+ error = uiomove(buff, buff_idx, uio);
+ if (error) {
+ goto get_out;
+ }
+ buff_idx = 0;
+ }
+
+ if (amt == 0) { // just in case...
+ break;
+ }
+ }
+
+ get_out:
+ *_buff_idx = buff_idx;
+
+ return error;
+}
+
+
+static int copy_out_kfse(fs_event_watcher *watcher, kfs_event *kfse, struct uio *uio) __attribute__((noinline));
+
+static int
+copy_out_kfse(fs_event_watcher *watcher, kfs_event *kfse, struct uio *uio)
+{
+ int error;
+ uint16_t tmp16;
+ int32_t type;
+ kfs_event *cur;
+ char evbuff[512];
+ int evbuff_idx = 0;
+
+ if (kfse->type == FSE_INVALID) {
+ panic("fsevents: copy_out_kfse: asked to copy out an invalid event (kfse %p, refcount %d fref ptr %p)\n", kfse, kfse->refcount, kfse->str);
+ }
+
+ if (kfse->flags & KFSE_BEING_CREATED) {
+ return 0;
+ }
+
+ if (kfse->type == FSE_RENAME && kfse->dest == NULL) {
+ //
+ // This can happen if an event gets recycled but we had a
+ // pointer to it in our event queue. The event is the
+ // destination of a rename which we'll process separately
+ // (that is, another kfse points to this one so it's ok
+ // to skip this guy because we'll process it when we process
+ // the other one)
+ error = 0;
+ goto get_out;
+ }
+
+ if (watcher->flags & WATCHER_WANTS_EXTENDED_INFO) {
+
+ type = (kfse->type & 0xfff);
+
+ if (kfse->flags & KFSE_CONTAINS_DROPPED_EVENTS) {
+ type |= (FSE_CONTAINS_DROPPED_EVENTS << FSE_FLAG_SHIFT);
+ } else if (kfse->flags & KFSE_COMBINED_EVENTS) {
+ type |= (FSE_COMBINED_EVENTS << FSE_FLAG_SHIFT);
+ }
+
+ } else {
+ type = (int32_t)kfse->type;
+ }
+
+ // copy out the type of the event
+ memcpy(evbuff, &type, sizeof(int32_t));
+ evbuff_idx += sizeof(int32_t);
+
+ // copy out the pid of the person that generated the event
+ memcpy(&evbuff[evbuff_idx], &kfse->pid, sizeof(pid_t));
+ evbuff_idx += sizeof(pid_t);
+
+ cur = kfse;
+
+ copy_again:
+
+ if (kfse->type == FSE_DOCID_CHANGED || kfse->type == FSE_DOCID_CREATED) {
+ dev_t dev = cur->dev;
+ ino_t ino = cur->ino;
+ uint64_t ival;
+
+ error = fill_buff(FSE_ARG_DEV, sizeof(dev_t), &dev, evbuff, &evbuff_idx, sizeof(evbuff), uio);
+ if (error != 0) {
+ goto get_out;
+ }
+
+ error = fill_buff(FSE_ARG_INO, sizeof(ino_t), &ino, evbuff, &evbuff_idx, sizeof(evbuff), uio);
+ if (error != 0) {
+ goto get_out;
+ }
+
+ memcpy(&ino, &cur->str, sizeof(ino_t));
+ error = fill_buff(FSE_ARG_INO, sizeof(ino_t), &ino, evbuff, &evbuff_idx, sizeof(evbuff), uio);
+ if (error != 0) {
+ goto get_out;
+ }
+
+ memcpy(&ival, &cur->uid, sizeof(uint64_t)); // the docid gets stuffed into the ino field
+ error = fill_buff(FSE_ARG_INT64, sizeof(uint64_t), &ival, evbuff, &evbuff_idx, sizeof(evbuff), uio);
+ if (error != 0) {
+ goto get_out;
+ }
+
+ goto done;
+ }
+
+ if (cur->str == NULL || cur->str[0] == '\0') {
+ printf("copy_out_kfse:2: empty/short path (%s)\n", cur->str);
+ error = fill_buff(FSE_ARG_STRING, 2, "/", evbuff, &evbuff_idx, sizeof(evbuff), uio);
+ } else {
+ error = fill_buff(FSE_ARG_STRING, cur->len, cur->str, evbuff, &evbuff_idx, sizeof(evbuff), uio);
+ }
+ if (error != 0) {
+ goto get_out;
+ }
+
+ if (cur->dev == 0 && cur->ino == 0) {
+ // this happens when a rename event happens and the
+ // destination of the rename did not previously exist.
+ // it thus has no other file info so skip copying out
+ // the stuff below since it isn't initialized
+ goto done;
+ }
+
+
+ if (watcher->flags & WATCHER_WANTS_COMPACT_EVENTS) {
+ int32_t finfo_size;
+
+ finfo_size = sizeof(dev_t) + sizeof(ino64_t) + sizeof(int32_t) + sizeof(uid_t) + sizeof(gid_t);
+ error = fill_buff(FSE_ARG_FINFO, finfo_size, &cur->ino, evbuff, &evbuff_idx, sizeof(evbuff), uio);
+ if (error != 0) {
+ goto get_out;
+ }
+ } else {
+ ino_t ino;
+
+ error = fill_buff(FSE_ARG_DEV, sizeof(dev_t), &cur->dev, evbuff, &evbuff_idx, sizeof(evbuff), uio);
+ if (error != 0) {
+ goto get_out;
+ }
+
+ ino = (ino_t)cur->ino;
+ error = fill_buff(FSE_ARG_INO, sizeof(ino_t), &ino, evbuff, &evbuff_idx, sizeof(evbuff), uio);
+ if (error != 0) {
+ goto get_out;
+ }
+
+ error = fill_buff(FSE_ARG_MODE, sizeof(int32_t), &cur->mode, evbuff, &evbuff_idx, sizeof(evbuff), uio);
+ if (error != 0) {
+ goto get_out;
+ }
+
+ error = fill_buff(FSE_ARG_UID, sizeof(uid_t), &cur->uid, evbuff, &evbuff_idx, sizeof(evbuff), uio);
+ if (error != 0) {
+ goto get_out;
+ }
+
+ error = fill_buff(FSE_ARG_GID, sizeof(gid_t), &cur->gid, evbuff, &evbuff_idx, sizeof(evbuff), uio);
+ if (error != 0) {
+ goto get_out;
+ }
+ }
+
+
+ if (cur->dest) {
+ cur = cur->dest;
+ goto copy_again;
+ }
+
+ done:
+ // very last thing: the time stamp
+ error = fill_buff(FSE_ARG_INT64, sizeof(uint64_t), &cur->abstime, evbuff, &evbuff_idx, sizeof(evbuff), uio);
+ if (error != 0) {
+ goto get_out;
+ }
+
+ // check if the FSE_ARG_DONE will fit
+ if (sizeof(uint16_t) > sizeof(evbuff) - evbuff_idx) {
+ if (evbuff_idx > uio_resid(uio)) {
+ error = ENOSPC;
+ goto get_out;
+ }
+ error = uiomove(evbuff, evbuff_idx, uio);
+ if (error) {
+ goto get_out;
+ }
+ evbuff_idx = 0;
+ }
+
+ tmp16 = FSE_ARG_DONE;
+ memcpy(&evbuff[evbuff_idx], &tmp16, sizeof(uint16_t));
+ evbuff_idx += sizeof(uint16_t);
+
+ // flush any remaining data in the buffer (and hopefully
+ // in most cases this is the only uiomove we'll do)
+ if (evbuff_idx > uio_resid(uio)) {
+ error = ENOSPC;
+ } else {
+ error = uiomove(evbuff, evbuff_idx, uio);
+ }
+
+ get_out:
+
+ return error;
+}
+
+