ccn_versioning.c

Go to the documentation of this file.
00001 /**
00002  * @file ccn_versioning.c
00003  * @brief Versioning support.
00004  * 
00005  * Part of the CCNx C Library.
00006  *
00007  * Copyright (C) 2009-2010 Palo Alto Research Center, Inc.
00008  *
00009  * This library is free software; you can redistribute it and/or modify it
00010  * under the terms of the GNU Lesser General Public License version 2.1
00011  * as published by the Free Software Foundation.
00012  * This library is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00015  * Lesser General Public License for more details. You should have received
00016  * a copy of the GNU Lesser General Public License along with this library;
00017  * if not, write to the Free Software Foundation, Inc., 51 Franklin Street,
00018  * Fifth Floor, Boston, MA 02110-1301 USA.
00019  */
00020 #include <errno.h>
00021 #include <stdio.h>
00022 #include <stdlib.h>
00023 #include <string.h>
00024 #include <unistd.h>
00025 #include <ccn/bloom.h>
00026 #include <ccn/ccn.h>
00027 #include <ccn/charbuf.h>
00028 #include <ccn/uri.h>
00029 #include <ccn/ccn_private.h>
00030 
00031 #define FF 0xff
00032 
00033 /**
00034  * This appends a filter useful for
00035  * excluding everything between two 'fenceposts' in an Exclude construct.
00036  */
00037 static void
00038 append_filter_all(struct ccn_charbuf *c)
00039 {
00040     ccn_charbuf_append_tt(c, CCN_DTAG_Any, CCN_DTAG);
00041     ccn_charbuf_append_closer(c);
00042 }
00043 
00044 /**
00045  * Append AnswerOriginKind=1 to partially constructed Interest, meaning
00046  * do not generate new content.
00047  */
00048 static void
00049 answer_passive(struct ccn_charbuf *templ)
00050 {
00051     ccn_charbuf_append_tt(templ, CCN_DTAG_AnswerOriginKind, CCN_DTAG);
00052     ccn_charbuf_append_tt(templ, 1, CCN_UDATA);
00053     ccn_charbuf_append(templ, "1", 1);
00054     ccn_charbuf_append_closer(templ); /* </AnswerOriginKind> */
00055 }
00056 
00057 /**
00058  * Append ChildSelector to partially constructed Interest, meaning
00059  * prefer to send rightmost available.
00060  */
00061 static void
00062 answer_highest(struct ccn_charbuf *templ)
00063 {
00064     ccnb_tagged_putf(templ, CCN_DTAG_ChildSelector, "1");
00065 }
00066 
00067 static void
00068 append_future_vcomp(struct ccn_charbuf *templ)
00069 {
00070     /* One beyond a distant future version stamp */
00071     unsigned char b[7] = {CCN_MARKER_VERSION + 1, 0, 0, 0, 0, 0, 0};
00072     ccn_charbuf_append_tt(templ, CCN_DTAG_Component, CCN_DTAG);
00073     ccn_charbuf_append_tt(templ, sizeof(b), CCN_BLOB);
00074     ccn_charbuf_append(templ, b, sizeof(b));
00075     ccn_charbuf_append_closer(templ); /* </Component> */
00076 }
00077 
00078 static struct ccn_charbuf *
00079 resolve_templ(struct ccn_charbuf *templ, unsigned const char *vcomp, int size)
00080 {
00081     if (templ == NULL)
00082         templ = ccn_charbuf_create();
00083     if (size < 3 || size > 16) {
00084         ccn_charbuf_destroy(&templ);
00085         return(NULL);
00086     }
00087     templ->length = 0;
00088     ccn_charbuf_append_tt(templ, CCN_DTAG_Interest, CCN_DTAG);
00089     ccn_charbuf_append_tt(templ, CCN_DTAG_Name, CCN_DTAG);
00090     ccn_charbuf_append_closer(templ); /* </Name> */
00091     ccn_charbuf_append_tt(templ, CCN_DTAG_Exclude, CCN_DTAG);
00092     append_filter_all(templ);
00093     ccn_charbuf_append_tt(templ, CCN_DTAG_Component, CCN_DTAG);
00094     ccn_charbuf_append_tt(templ, size, CCN_BLOB);
00095     ccn_charbuf_append(templ, vcomp, size);
00096     ccn_charbuf_append_closer(templ); /* </Component> */
00097     append_future_vcomp(templ);
00098     append_filter_all(templ);
00099     ccn_charbuf_append_closer(templ); /* </Exclude> */
00100     answer_highest(templ);
00101     answer_passive(templ);
00102     ccn_charbuf_append_closer(templ); /* </Interest> */
00103     return(templ);
00104 }
00105 
00106 /**
00107  * Resolve the version, based on existing ccn content.
00108  * @param h is the the ccn handle; it may be NULL, but it is preferable to
00109  *        use the handle that the client probably already has.
00110  * @param name is a ccnb-encoded Name prefix. It gets extended in-place with
00111  *        one additional Component such that it names highest extant
00112  *        version that can be found, subject to the supplied timeout.
00113  * @param versioning_flags presently must be CCN_V_HIGH or CCN_V_HIGHEST,
00114  *        possibly combined with CCN_V_NESTOK.  If CCN_V_NESTOK is not present
00115  *        and the ending component appears to be a version, the routine
00116  *        returns 0 immediately, on the assumption that an explicit
00117  *        version has already been provided.
00118  * @param timeout_ms is a time value in milliseconds. This is applied per
00119  *        fetch attempt, so the total time may be longer by a factor that
00120  *        depends on the number of (ccn) hops to the source(s).
00121  * @returns -1 for error, 0 if name could not be extended, 1 if was.
00122  */
00123 int
00124 ccn_resolve_version(struct ccn *h, struct ccn_charbuf *name,
00125                     int versioning_flags, int timeout_ms)
00126 {
00127     int res;
00128     int myres = -1;
00129     struct ccn_parsed_ContentObject pco_space = { 0 };
00130     struct ccn_charbuf *templ = NULL;
00131     struct ccn_charbuf *prefix = ccn_charbuf_create();
00132     struct ccn_charbuf *cobj = ccn_charbuf_create();
00133     struct ccn_parsed_ContentObject *pco = &pco_space;
00134     struct ccn_indexbuf *ndx = ccn_indexbuf_create();
00135     const unsigned char *vers = NULL;
00136     size_t vers_size = 0;
00137     int n;
00138     struct ccn_indexbuf *nix = ccn_indexbuf_create();
00139     unsigned char lowtime[7] = {CCN_MARKER_VERSION, 0, FF, FF, FF, FF, FF};
00140     
00141     if ((versioning_flags & ~CCN_V_NESTOK & ~CCN_V_EST) != CCN_V_HIGH) {
00142         ccn_seterror(h, EINVAL);
00143         ccn_perror(h, "ccn_resolve_version is only implemented for versioning_flags = CCN_V_HIGH(EST)");
00144         goto Finish;
00145     }
00146     n = ccn_name_split(name, nix);
00147     if (n < 0)
00148         goto Finish;
00149     if ((versioning_flags & CCN_V_NESTOK) == 0) {
00150         res = ccn_name_comp_get(name->buf, nix, n - 1, &vers, &vers_size);
00151         if (res >= 0 && vers_size == 7 && vers[0] == CCN_MARKER_VERSION) {
00152             myres = 0;
00153             goto Finish;
00154         }    
00155     }
00156     templ = resolve_templ(templ, lowtime, sizeof(lowtime));
00157     ccn_charbuf_append(prefix, name->buf, name->length); /* our copy */
00158     cobj->length = 0;
00159     res = ccn_get(h, prefix, templ, timeout_ms, cobj, pco, ndx, 0);
00160     while (cobj->length != 0) {
00161         if (pco->type == CCN_CONTENT_NACK) // XXX - also check for number of components
00162             break;
00163         res = ccn_name_comp_get(cobj->buf, ndx, n, &vers, &vers_size);
00164         if (res < 0)
00165             break;
00166         if (vers_size == 7 && vers[0] == CCN_MARKER_VERSION) {
00167             /* Looks like we have versions. */
00168             name->length = 0;
00169             ccn_charbuf_append(name, prefix->buf, prefix->length);
00170             ccn_name_append(name, vers, vers_size);
00171             myres = 0;
00172             if ((versioning_flags & CCN_V_EST) == 0)
00173                 break;
00174             templ = resolve_templ(templ, vers, vers_size);
00175             if (templ == NULL) break;
00176             cobj->length = 0;
00177             res = ccn_get(h, prefix, templ, timeout_ms, cobj, pco, ndx,
00178                           CCN_GET_NOKEYWAIT);
00179         }
00180         else break;
00181     }
00182 Finish:
00183     ccn_charbuf_destroy(&prefix);
00184     ccn_charbuf_destroy(&cobj);
00185     ccn_indexbuf_destroy(&ndx);
00186     ccn_indexbuf_destroy(&nix);
00187     ccn_charbuf_destroy(&templ);
00188     return(myres);
00189 }
00190 
00191 /**
00192  * Extend a Name with a new version stamp
00193  * @param h is the the ccn handle.
00194  *        May be NULL.  This procedure does not use the connection.
00195  * @param name is a ccnb-encoded Name prefix. By default it gets extended
00196  *        in-place with one additional Component that conforms to the
00197  *        versioning profile and is based on the supplied time, unless a
00198  *        version component is already present.
00199  * @param versioning_flags modifies the default behavior:
00200  *        CCN_V_REPLACE causes the last component to be replaced if it
00201  *        appears to be a version stamp.  If CCN_V_HIGH is set as well, an
00202  *        attempt will be made to generate a new version stamp that is
00203  *        later than the existing one, or to return an error.
00204  *        CCN_V_NOW bases the version on the current time rather than the
00205  *        supplied time.
00206  *        CCN_V_NESTOK will allow the new version component to be appended
00207  *        even if there is one there (this makes no difference if CCN_V_REPLACE
00208  *        is also set).
00209  * @param secs is the desired time, in seconds since epoch
00210  *        (ignored if CCN_V_NOW is set).
00211  * @param nsecs is the number of nanoseconds.
00212  * @returns -1 for error, 0 for success.
00213  */
00214 int
00215 ccn_create_version(struct ccn *h, struct ccn_charbuf *name,
00216                    int versioning_flags, intmax_t secs, int nsecs)
00217 {
00218     size_t i;
00219     size_t j;
00220     size_t lc = 0;
00221     size_t oc = 0;
00222     int n;
00223     struct ccn_indexbuf *nix = NULL;
00224     int myres = -1;
00225     int already_versioned = 0;
00226     int ok_flags = (CCN_V_REPLACE | CCN_V_HIGH | CCN_V_NOW | CCN_V_NESTOK);
00227     // XXX - right now we ignore h, but in the future we may use it to try to avoid non-monotonicies in the versions.
00228     
00229     nix = ccn_indexbuf_create();
00230     n = ccn_name_split(name, nix);
00231     if (n < 0)
00232         goto Finish;
00233     if ((versioning_flags & ~ok_flags) != 0)
00234         goto Finish;        
00235     /* Check for existing version component */
00236     if (n >= 1) {
00237         oc = nix->buf[n-1];
00238         lc = nix->buf[n] - oc;
00239         if (lc <= 11 && lc >= 6 && name->buf[oc + 2] == CCN_MARKER_VERSION)
00240             already_versioned = 1;
00241     }
00242     myres = 0;
00243     if (already_versioned &&
00244         (versioning_flags & (CCN_V_REPLACE | CCN_V_NESTOK)) == 0)
00245         goto Finish;
00246     name->length -= 1; /* Strip name closer */
00247     i = name->length;
00248     myres |= ccn_charbuf_append_tt(name, CCN_DTAG_Component, CCN_DTAG);
00249     if ((versioning_flags & CCN_V_NOW) != 0)
00250         myres |= ccnb_append_now_blob(name, CCN_MARKER_VERSION);
00251     else {
00252         myres |= ccnb_append_timestamp_blob(name, CCN_MARKER_VERSION, secs, nsecs);
00253     }
00254     myres |= ccn_charbuf_append_closer(name); /* </Component> */
00255     if (myres < 0) {
00256         name->length = i;
00257         goto CloseName;
00258     }
00259     j = name->length;
00260     if (already_versioned && (versioning_flags & CCN_V_REPLACE) != 0) {
00261         oc = nix->buf[n-1];
00262         lc = nix->buf[n] - oc;
00263         if ((versioning_flags & CCN_V_HIGH) != 0 &&
00264             memcmp(name->buf + oc, name->buf + i, j - i) > 0) {
00265             /* Supplied version is in the future. */
00266             name->length = i;
00267             // XXX - we could try harder to make this work, for now just error out
00268             myres = -1;
00269             goto CloseName;
00270         }
00271         memmove(name->buf + oc, name->buf + i, j - i);
00272         name->length -= lc;
00273     }
00274 CloseName:
00275     myres |= ccn_charbuf_append_closer(name); /* </Name> */
00276 Finish:
00277     myres = (myres < 0) ? -1 : 0;
00278     ccn_indexbuf_destroy(&nix);
00279     return(myres);
00280 }
Generated on Fri May 13 16:27:03 2011 for Content-Centric Networking in C by  doxygen 1.6.3