• Main Page
  • Related Pages
  • Namespaces
  • Data Structures
  • Files
  • File List
  • Globals

ink_file.cc

Go to the documentation of this file.
00001 /** @file
00002 
00003   File manipulation routines for libts
00004 
00005   @section license License
00006 
00007   Licensed to the Apache Software Foundation (ASF) under one
00008   or more contributor license agreements.  See the NOTICE file
00009   distributed with this work for additional information
00010   regarding copyright ownership.  The ASF licenses this file
00011   to you under the Apache License, Version 2.0 (the
00012   "License"); you may not use this file except in compliance
00013   with the License.  You may obtain a copy of the License at
00014 
00015       http://www.apache.org/licenses/LICENSE-2.0
00016 
00017   Unless required by applicable law or agreed to in writing, software
00018   distributed under the License is distributed on an "AS IS" BASIS,
00019   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00020   See the License for the specific language governing permissions and
00021   limitations under the License.
00022  */
00023 
00024 #include "libts.h"
00025 
00026 #if HAVE_SYS_STAT_H
00027 #include <sys/stat.h>
00028 #endif
00029 
00030 int
00031 ink_fputln(FILE * stream, const char *s)
00032 {
00033   if (stream && s) {
00034     int rc = fputs(s, stream);
00035     if (rc > 0)
00036       rc += fputc('\n', stream);
00037     return rc;
00038   }
00039   else
00040     return -EINVAL;
00041 }                               /* End ink_fgets */
00042 
00043 /*---------------------------------------------------------------------------*
00044 
00045   int ink_file_fd_readline(int fd, int bufsz, char *buf)
00046 
00047   This routine reads bytes from <fd> into the buffer pointed to by <buf>.
00048   The reading stops when (a) a LF is read into the buffer, (b) the end of
00049   file is reached, or (c) <bufsz> - 1 bytes are read.  The <bufsz> parameter
00050   must be >= 2.
00051 
00052   The data pointed to by <buf> is always NUL terminated, and the LF is
00053   left in the data.  This routine can be used as a replacement for
00054   fgets-like functions.  If the <bufsz> is too small to hold a complete line,
00055   the partial line will be stored, and subsequent reads will read more data.
00056 
00057   This routine returns the number of bytes read, 0 on end of file, or
00058   a negative errno on error.
00059 
00060  *---------------------------------------------------------------------------*/
00061 
00062 int
00063 ink_file_fd_readline(int fd, int bufsz, char *buf)
00064 {
00065   char c;
00066   int i = 0;
00067 
00068   if (bufsz < 2)
00069     return (-EINVAL);           /* bufsz must by >= 2 */
00070 
00071   while (i < bufsz - 1) {       /* leave 1 byte for NUL */
00072     int n = read(fd, &c, 1);    /* read 1 byte */
00073     if (n == 0)
00074       break;                    /* EOF */
00075     if (n < 0)
00076       return (n);               /* error */
00077     buf[i++] = c;               /* store in buffer */
00078     if (c == '\n')
00079       break;                    /* stop if stored a LF */
00080   }
00081 
00082   buf[i] = '\0';                /* NUL terminate buffer */
00083   return (i);                   /* number of bytes read */
00084 }                               /* End ink_file_fd_readline */
00085 
00086 /* Write until NUL */
00087 int
00088 ink_file_fd_writestring(int fd, const char *buf)
00089 {
00090   int len, i = 0;
00091 
00092   if (buf && (len = strlen(buf)) > 0 && (i = (int) write(fd, buf, (size_t) len) != len))
00093     i = -1;
00094 
00095   return i;                     /* return chars written */
00096 }                               /* End ink_file_fd_writestring */
00097 
00098 int
00099 ink_filepath_merge(char *path, int pathsz, const char *rootpath, const char *addpath, int flags)
00100 {
00101   size_t rootlen; // is the length of the src rootpath
00102   size_t maxlen;  // maximum total path length
00103   size_t keptlen; // is the length of the retained rootpath
00104   size_t pathlen; // is the length of the result path
00105   size_t seglen;  // is the end of the current segment
00106   char curdir[PATH_NAME_MAX];
00107 
00108   /* Treat null as an empty path.
00109   */
00110   if (!addpath)
00111     addpath = "";
00112 
00113   if (addpath[0] == '/') {
00114     // If addpath is rooted, then rootpath is unused.
00115     // Ths violates any INK_FILEPATH_SECUREROOTTEST and
00116     // INK_FILEPATH_NOTABSOLUTE flags specified.
00117     //
00118     if (flags & INK_FILEPATH_SECUREROOTTEST)
00119       return EACCES; // APR_EABOVEROOT;
00120     if (flags & INK_FILEPATH_NOTABSOLUTE)
00121       return EISDIR; // APR_EABSOLUTE;
00122 
00123     // If INK_FILEPATH_NOTABOVEROOT wasn't specified,
00124     // we won't test the root again, it's ignored.
00125     // Waste no CPU retrieving the working path.
00126     //
00127     if (!rootpath && !(flags & INK_FILEPATH_NOTABOVEROOT))
00128       rootpath = "";
00129   }
00130   else {
00131     // If INK_FILEPATH_NOTABSOLUTE is specified, the caller
00132     // requires a relative result.  If the rootpath is
00133     // ommitted, we do not retrieve the working path,
00134     // if rootpath was supplied as absolute then fail.
00135     //
00136     if (flags & INK_FILEPATH_NOTABSOLUTE) {
00137       if (!rootpath)
00138         rootpath = "";
00139       else if (rootpath[0] == '/')
00140         return EISDIR; //APR_EABSOLUTE;
00141     }
00142   }
00143   if (!rootpath) {
00144     // Start with the current working path.  This is bass akwards,
00145     // but required since the compiler (at least vc) doesn't like
00146     // passing the address of a char const* for a char** arg.
00147     //
00148     if (!getcwd(curdir, sizeof(curdir))) {
00149       return errno;
00150     }
00151     rootpath = curdir;
00152   }
00153   rootlen = strlen(rootpath);
00154   maxlen = rootlen + strlen(addpath) + 4; // 4 for slashes at start, after
00155                                           // root, and at end, plus trailing
00156                                           // null
00157   if (maxlen > (size_t)pathsz) {
00158     return E2BIG; //APR_ENAMETOOLONG;
00159   }
00160   if (addpath[0] == '/') {
00161     // Ignore the given root path, strip off leading
00162     // '/'s to a single leading '/' from the addpath,
00163     // and leave addpath at the first non-'/' character.
00164     //
00165     keptlen = 0;
00166     while (addpath[0] == '/')
00167       ++addpath;
00168     path[0] = '/';
00169     pathlen = 1;
00170   }
00171   else {
00172     // If both paths are relative, fail early
00173     //
00174     if (rootpath[0] != '/' && (flags & INK_FILEPATH_NOTRELATIVE))
00175       return EBADF; //APR_ERELATIVE;
00176 
00177     // Base the result path on the rootpath
00178     //
00179     keptlen = rootlen;
00180     memcpy(path, rootpath, rootlen);
00181 
00182     // Always '/' terminate the given root path
00183     //
00184     if (keptlen && path[keptlen - 1] != '/') {
00185       path[keptlen++] = '/';
00186     }
00187     pathlen = keptlen;
00188   }
00189 
00190   while (*addpath) {
00191     // Parse each segment, find the closing '/'
00192     //
00193     const char *next = addpath;
00194     while (*next && (*next != '/')) {
00195       ++next;
00196     }
00197     seglen = next - addpath;
00198 
00199     if (seglen == 0 || (seglen == 1 && addpath[0] == '.')) {
00200       // noop segment (/ or ./) so skip it
00201       //
00202     }
00203     else if (seglen == 2 && addpath[0] == '.' && addpath[1] == '.') {
00204       // backpath (../)
00205       if (pathlen == 1 && path[0] == '/') {
00206         // Attempt to move above root.  Always die if the
00207         // INK_FILEPATH_SECUREROOTTEST flag is specified.
00208         //
00209         if (flags & INK_FILEPATH_SECUREROOTTEST) {
00210           return EACCES; //APR_EABOVEROOT;
00211         }
00212 
00213         // Otherwise this is simply a noop, above root is root.
00214         // Flag that rootpath was entirely replaced.
00215         //
00216         keptlen = 0;
00217       }
00218       else if (pathlen == 0
00219                || (pathlen == 3
00220                && !memcmp(path + pathlen - 3, "../", 3))
00221                || (pathlen  > 3
00222                && !memcmp(path + pathlen - 4, "/../", 4))) {
00223         // Path is already backpathed or empty, if the
00224         // INK_FILEPATH_SECUREROOTTEST.was given die now.
00225         //
00226         if (flags & INK_FILEPATH_SECUREROOTTEST) {
00227           return EACCES; //APR_EABOVEROOT;
00228         }
00229 
00230         // Otherwise append another backpath, including
00231         // trailing slash if present.
00232         //
00233         memcpy(path + pathlen, "../", *next ? 3 : 2);
00234         pathlen += *next ? 3 : 2;
00235       }
00236       else {
00237         // otherwise crop the prior segment
00238         //
00239         do {
00240             --pathlen;
00241         } while (pathlen && path[pathlen - 1] != '/');
00242       }
00243 
00244       // Now test if we are above where we started and back up
00245       // the keptlen offset to reflect the added/altered path.
00246       //
00247       if (pathlen < keptlen) {
00248         if (flags & INK_FILEPATH_SECUREROOTTEST) {
00249           return EACCES; //APR_EABOVEROOT;
00250         }
00251         keptlen = pathlen;
00252       }
00253     }
00254     else {
00255         // An actual segment, append it to the destination path
00256         //
00257       if (*next) {
00258         seglen++;
00259       }
00260       memcpy(path + pathlen, addpath, seglen);
00261       pathlen += seglen;
00262     }
00263 
00264     // Skip over trailing slash to the next segment
00265     //
00266     if (*next) {
00267       ++next;
00268     }
00269 
00270     addpath = next;
00271   }
00272   path[pathlen] = '\0';
00273   if (pathlen > 1 && path[pathlen - 1] == '/') {
00274     // Trim trailing slash unless requested
00275     size_t es = strlen(addpath);
00276     if (es == 0 || addpath[es - 1] != '/') {
00277       --pathlen;
00278       path[pathlen] = '\0';
00279     }
00280   }
00281 
00282   // keptlen will be the rootlen unless the addpath contained
00283   // backpath elements.  If so, and INK_FILEPATH_NOTABOVEROOT
00284   // is specified (INK_FILEPATH_SECUREROOTTEST was caught above),
00285   // compare the original root to assure the result path is
00286   // still within given root path.
00287   //
00288   if ((flags & INK_FILEPATH_NOTABOVEROOT) && keptlen < rootlen) {
00289     if (strncmp(rootpath, path, rootlen)) {
00290       return EACCES; //APR_EABOVEROOT;
00291     }
00292     if (rootpath[rootlen - 1] != '/'
00293         && path[rootlen] && path[rootlen] != '/') {
00294       return EACCES; //APR_EABOVEROOT;
00295     }
00296   }
00297 
00298   return 0;
00299 }
00300 
00301 int
00302 ink_filepath_make(char *path, int pathsz, const char *rootpath, const char *addpath)
00303 {
00304   size_t rootlen; // is the length of the src rootpath
00305   size_t maxlen;  // maximum total path length
00306 
00307   /* Treat null as an empty path.
00308   */
00309   if (!addpath)
00310     addpath = "";
00311 
00312   if (addpath[0] == '/') {
00313     // If addpath is rooted, then rootpath is unused.
00314     ink_strlcpy(path, addpath, pathsz);
00315     return 0;
00316   }
00317   if (!rootpath || !*rootpath) {
00318     // If there's no rootpath return the addpath
00319     ink_strlcpy(path, addpath, pathsz);
00320     return 0;
00321   }
00322   rootlen = strlen(rootpath);
00323   maxlen  = strlen(addpath) + 2;
00324   if (maxlen > (size_t)pathsz) {
00325     *path = '\0';
00326     return (int)maxlen;
00327   }
00328   ink_strlcpy(path, rootpath, pathsz);
00329   path += rootlen;
00330   pathsz -= rootlen;
00331   if (*(path - 1) != '/') {
00332     *(path++) = '/';
00333     --pathsz;
00334   }
00335   ink_strlcpy(path, addpath, pathsz);
00336   return 0;
00337 }
00338 
00339 int
00340 ink_file_fd_zerofill(int fd, off_t size)
00341 {
00342   // Clear the file by truncating it to zero and then to the desired size.
00343   if (ftruncate(fd, 0) < 0) {
00344     return errno;
00345   }
00346 
00347   // ZFS does not implement posix_fallocate() and fails with EINVAL. As a general workaround,
00348   // just fall back to ftrucate if the preallocation fails.
00349 #if HAVE_POSIX_FALLOCATE
00350   if (posix_fallocate(fd, 0, size) == 0) {
00351     return 0;
00352   }
00353 #endif
00354 
00355   if (ftruncate(fd, size) < 0) {
00356     return errno;
00357   }
00358 
00359   return 0;
00360 }
00361 
00362 bool
00363 ink_file_is_directory(const char * path)
00364 {
00365   struct stat sbuf;
00366 
00367   if (stat(path, &sbuf) == -1) {
00368     return false;
00369   }
00370 
00371   return S_ISDIR(sbuf.st_mode);
00372 }

Generated by  doxygen 1.7.1