+
+/*
+ * Per HI and Finder requirements, HFS should add in the
+ * date/time that a particular directory entry was added
+ * to the containing directory.
+ * This is stored in the extended Finder Info for the
+ * item in question.
+ *
+ * Note that this field is also set explicitly in the hfs_vnop_setxattr code.
+ * We must ignore user attempts to set this part of the finderinfo, and
+ * so we need to save a local copy of the date added, write in the user
+ * finderinfo, then stuff the value back in.
+ */
+void hfs_write_dateadded (struct cat_attr *attrp, u_int32_t dateadded) {
+ u_int8_t *finfo = NULL;
+
+ /* overlay the FinderInfo to the correct pointer, and advance */
+ finfo = (u_int8_t*)attrp->ca_finderinfo;
+ finfo = finfo + 16;
+
+ /*
+ * Make sure to write it out as big endian, since that's how
+ * finder info is defined.
+ *
+ * NOTE: This is a Unix-epoch timestamp, not a HFS/Traditional Mac timestamp.
+ */
+ if (S_ISREG(attrp->ca_mode)) {
+ struct FndrExtendedFileInfo *extinfo = (struct FndrExtendedFileInfo *)finfo;
+ extinfo->date_added = OSSwapHostToBigInt32(dateadded);
+ attrp->ca_recflags |= kHFSHasDateAddedMask;
+ }
+ else if (S_ISDIR(attrp->ca_mode)) {
+ struct FndrExtendedDirInfo *extinfo = (struct FndrExtendedDirInfo *)finfo;
+ extinfo->date_added = OSSwapHostToBigInt32(dateadded);
+ attrp->ca_recflags |= kHFSHasDateAddedMask;
+ }
+ /* If it were neither directory/file, then we'd bail out */
+ return;
+}
+
+static u_int32_t
+hfs_get_dateadded_internal(const uint8_t *finderinfo, mode_t mode)
+{
+ const uint8_t *finfo = NULL;
+ u_int32_t dateadded = 0;
+
+
+
+ /* overlay the FinderInfo to the correct pointer, and advance */
+ finfo = finderinfo + 16;
+
+ /*
+ * FinderInfo is written out in big endian... make sure to convert it to host
+ * native before we use it.
+ */
+ if (S_ISREG(mode)) {
+ const struct FndrExtendedFileInfo *extinfo = (const struct FndrExtendedFileInfo *)finfo;
+ dateadded = OSSwapBigToHostInt32 (extinfo->date_added);
+ }
+ else if (S_ISDIR(mode)) {
+ const struct FndrExtendedDirInfo *extinfo = (const struct FndrExtendedDirInfo *)finfo;
+ dateadded = OSSwapBigToHostInt32 (extinfo->date_added);
+ }
+
+ return dateadded;
+}
+
+u_int32_t
+hfs_get_dateadded(struct cnode *cp)
+{
+ if ((cp->c_attr.ca_recflags & kHFSHasDateAddedMask) == 0) {
+ /* Date added was never set. Return 0. */
+ return (0);
+ }
+
+ return (hfs_get_dateadded_internal((u_int8_t*)cp->c_finderinfo,
+ cp->c_attr.ca_mode));
+}
+
+u_int32_t
+hfs_get_dateadded_from_blob(const uint8_t *finderinfo, mode_t mode)
+{
+ return (hfs_get_dateadded_internal(finderinfo, mode));
+}
+
+/*
+ * Per HI and Finder requirements, HFS maintains a "write/generation
+ * count" for each file that is incremented on any write & pageout.
+ * It should start at 1 to reserve "0" as a special value. If it
+ * should ever wrap around, it will skip using 0.
+ *
+ * Note that finderinfo is manipulated in hfs_vnop_setxattr and care
+ * is and should be taken to ignore user attempts to set the part of
+ * the finderinfo that records the generation counter.
+ *
+ * Any change to the generation counter *must* not be visible before
+ * the change that caused it (for obvious reasons), and given the
+ * limitations of our current architecture, the change to the
+ * generation counter may occur some time afterwards (particularly in
+ * the case where a file is mapped writable---more on that below).
+ *
+ * We make no guarantees about the consistency of a file. In other
+ * words, a reader that is operating concurrently with a writer might
+ * see some, but not all of writer's changes, and the generation
+ * counter will *not* necessarily tell you this has happened. To
+ * enforce consistency, clients must make their own arrangements
+ * e.g. use file locking.
+ *
+ * We treat files that are mapped writable as a special case: when
+ * that happens, clients requesting the generation count will be told
+ * it has a generation count of zero and they use that knowledge as a
+ * hint that the file is changing and it therefore might be prudent to
+ * wait until it is no longer mapped writable. Clients should *not*
+ * rely on this behaviour however; we might decide that it's better
+ * for us to publish the fact that a file is mapped writable via
+ * alternate means and return the generation counter when it is mapped
+ * writable as it still has some, albeit limited, use. We reserve the
+ * right to make this change.
+ *
+ * Lastly, it's important to realise that because data and metadata
+ * take different paths through the system, it's possible upon crash
+ * or sudden power loss and after a restart, that a change may be
+ * visible to the rest of the system without a corresponding change to
+ * the generation counter. The reverse may also be true, but for all
+ * practical applications this shouldn't be an issue.
+ */
+void hfs_write_gencount (struct cat_attr *attrp, uint32_t gencount) {
+ u_int8_t *finfo = NULL;
+
+ /* overlay the FinderInfo to the correct pointer, and advance */
+ finfo = (u_int8_t*)attrp->ca_finderinfo;
+ finfo = finfo + 16;
+
+ /*
+ * Make sure to write it out as big endian, since that's how
+ * finder info is defined.
+ *
+ * Generation count is only supported for files.
+ */
+ if (S_ISREG(attrp->ca_mode)) {
+ struct FndrExtendedFileInfo *extinfo = (struct FndrExtendedFileInfo *)finfo;
+ extinfo->write_gen_counter = OSSwapHostToBigInt32(gencount);
+ }
+
+ /* If it were neither directory/file, then we'd bail out */
+ return;
+}
+
+/*
+ * Increase the gen count by 1; if it wraps around to 0, increment by
+ * two. The cnode *must* be locked exclusively by the caller.
+ *
+ * You may think holding the lock is unnecessary because we only need
+ * to change the counter, but consider this sequence of events: thread
+ * A calls hfs_incr_gencount and the generation counter is 2 upon
+ * entry. A context switch occurs and thread B increments the counter
+ * to 3, thread C now gets the generation counter (for whatever
+ * purpose), and then another thread makes another change and the
+ * generation counter is incremented again---it's now 4. Now thread A
+ * continues and it sets the generation counter back to 3. So you can
+ * see, thread C would miss the change that caused the generation
+ * counter to increment to 4 and for this reason the cnode *must*
+ * always be locked exclusively.
+ */
+uint32_t hfs_incr_gencount (struct cnode *cp) {
+ u_int8_t *finfo = NULL;
+ u_int32_t gcount = 0;
+
+ /* overlay the FinderInfo to the correct pointer, and advance */
+ finfo = (u_int8_t*)cp->c_finderinfo;
+ finfo = finfo + 16;
+
+ /*
+ * FinderInfo is written out in big endian... make sure to convert it to host
+ * native before we use it.
+ *
+ * NOTE: the write_gen_counter is stored in the same location in both the
+ * FndrExtendedFileInfo and FndrExtendedDirInfo structs (it's the
+ * last 32-bit word) so it is safe to have one code path here.
+ */
+ if (S_ISDIR(cp->c_attr.ca_mode) || S_ISREG(cp->c_attr.ca_mode)) {
+ struct FndrExtendedFileInfo *extinfo = (struct FndrExtendedFileInfo *)finfo;
+ gcount = OSSwapBigToHostInt32 (extinfo->write_gen_counter);
+
+ /* Was it zero to begin with (file originated in 10.8 or earlier?) */
+ if (gcount == 0) {
+ gcount++;
+ }
+
+ /* now bump it */
+ gcount++;
+
+ /* Did it wrap around ? */
+ if (gcount == 0) {
+ gcount++;
+ }
+ extinfo->write_gen_counter = OSSwapHostToBigInt32 (gcount);
+
+ SET(cp->c_flag, C_MINOR_MOD);
+ }
+ else {
+ gcount = 0;
+ }
+
+ return gcount;
+}
+
+/*
+ * There is no need for any locks here (other than an iocount on an
+ * associated vnode) because reading and writing an aligned 32 bit
+ * integer should be atomic on all platforms we support.
+ */
+static u_int32_t
+hfs_get_gencount_internal(const uint8_t *finderinfo, mode_t mode)
+{
+ const uint8_t *finfo = NULL;
+ u_int32_t gcount = 0;
+
+ /* overlay the FinderInfo to the correct pointer, and advance */
+ finfo = finderinfo;
+ finfo = finfo + 16;
+
+ /*
+ * FinderInfo is written out in big endian... make sure to convert it to host
+ * native before we use it.
+ *
+ * NOTE: the write_gen_counter is stored in the same location in both the
+ * FndrExtendedFileInfo and FndrExtendedDirInfo structs (it's the
+ * last 32-bit word) so it is safe to have one code path here.
+ */
+ if (S_ISDIR(mode) || S_ISREG(mode)) {
+ const struct FndrExtendedFileInfo *extinfo = (const struct FndrExtendedFileInfo *)finfo;
+ gcount = OSSwapBigToHostInt32 (extinfo->write_gen_counter);
+
+ /*
+ * Is it zero? File might originate in 10.8 or earlier. We lie and bump it to 1,
+ * since the incrementer code is able to handle this case and will double-increment
+ * for us.
+ */
+ if (gcount == 0) {
+ gcount++;
+ }
+ }
+
+ return gcount;
+}
+
+/* Getter for the gen count */
+u_int32_t hfs_get_gencount (struct cnode *cp) {
+ return hfs_get_gencount_internal(cp->c_finderinfo, cp->c_attr.ca_mode);
+}
+
+/* Getter for the gen count from a buffer (currently pointer to finderinfo)*/
+u_int32_t hfs_get_gencount_from_blob (const uint8_t *finfoblob, mode_t mode) {
+ return hfs_get_gencount_internal(finfoblob, mode);
+}
+
+void hfs_clear_might_be_dirty_flag(cnode_t *cp)
+{
+ /*
+ * If we're about to touch both mtime and ctime, we can clear the
+ * C_MIGHT_BE_DIRTY_FROM_MAPPING since we can guarantee that
+ * subsequent page-outs can only be for data made dirty before
+ * now.
+ */
+ CLR(cp->c_flag, C_MIGHT_BE_DIRTY_FROM_MAPPING);
+}
+