LCOV - code coverage report
Current view: top level - vhd/lib - canonpath.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 30 69 43.5 %
Date: 2025-07-10 10:47:11 Functions: 2 2 100.0 %
Branches: 13 65 20.0 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * Copyright (c) 2016, Citrix Systems, Inc.
       3                 :            :  *
       4                 :            :  * All rights reserved.
       5                 :            :  *
       6                 :            :  * Redistribution and use in source and binary forms, with or without
       7                 :            :  * modification, are permitted provided that the following conditions are met:
       8                 :            :  * 
       9                 :            :  *  1. Redistributions of source code must retain the above copyright
      10                 :            :  *     notice, this list of conditions and the following disclaimer.
      11                 :            :  *  2. Redistributions in binary form must reproduce the above copyright
      12                 :            :  *     notice, this list of conditions and the following disclaimer in the
      13                 :            :  *     documentation and/or other materials provided with the distribution.
      14                 :            :  *  3. Neither the name of the copyright holder nor the names of its 
      15                 :            :  *     contributors may be used to endorse or promote products derived from 
      16                 :            :  *     this software without specific prior written permission.
      17                 :            :  *
      18                 :            :  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
      19                 :            :  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
      20                 :            :  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
      21                 :            :  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
      22                 :            :  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
      23                 :            :  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
      24                 :            :  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
      25                 :            :  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
      26                 :            :  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
      27                 :            :  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
      28                 :            :  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
      29                 :            :  */
      30                 :            : 
      31                 :            : #ifdef HAVE_CONFIG_H
      32                 :            : #include "config.h"
      33                 :            : #endif
      34                 :            : 
      35                 :            : #ifndef _GNU_SOURCE
      36                 :            :         #define _GNU_SOURCE
      37                 :            : #endif
      38                 :            : 
      39                 :            : #include <stdio.h>
      40                 :            : #include <errno.h>
      41                 :            : #include <stdlib.h>
      42                 :            : #include <string.h>
      43                 :            : #include <limits.h>
      44                 :            : #include <unistd.h>
      45                 :            : 
      46                 :            : #include "canonpath.h"
      47                 :            : #include "util.h"
      48                 :            : 
      49                 :            : char *
      50                 :          1 : normalize_path(const char *path, size_t path_len)
      51                 :            : {
      52                 :          1 :         size_t res_len = 0;
      53                 :            : 
      54                 :          1 :         const char *ptr = path;
      55                 :          1 :         const char *end = path + path_len;
      56                 :            :         const char *next;
      57                 :            : 
      58                 :            :         char *normalized_path;
      59 [ +  - ][ +  - ]:          1 :         if (!path_len || *path != '/') {
      60                 :            :                 // Relative path.
      61                 :          1 :                 char *wd = get_current_dir_name();
      62         [ +  - ]:          1 :                 if (!wd)
      63                 :            :                         return NULL;
      64                 :            : 
      65                 :          1 :                 res_len = strlen(wd);
      66                 :            :                 /* Add 2 to accomodate the / and the \0 */
      67         [ -  + ]:          1 :                 if (!(normalized_path = realloc(wd, res_len + path_len + 2))) {
      68                 :          0 :                         free(wd);
      69                 :          0 :                         return NULL;
      70                 :            :                 }
      71         [ #  # ]:          0 :         } else if (!(normalized_path = malloc(path_len + 1)))
      72                 :            :                 return NULL;
      73                 :            : 
      74         [ +  + ]:          2 :         for (ptr = path; ptr < end; ptr = next + 1) {
      75                 :            :                 size_t len;
      76         [ +  - ]:          1 :                 if (!(next = memchr(ptr, '/', end - ptr)))
      77                 :          1 :                         next = end;
      78                 :          1 :                 len = next - ptr;
      79   [ -  -  -  + ]:          1 :                 switch (len) {
      80                 :            :                         case 2:
      81 [ #  # ][ #  # ]:          0 :                                 if (ptr[0] == '.' && ptr[1] == '.') {
      82                 :          0 :                                         const char *slash = memrchr(normalized_path, '/', res_len);
      83         [ #  # ]:          0 :                                         if (slash)
      84                 :          0 :                                                 res_len = slash - normalized_path;
      85                 :          0 :                                         continue;
      86                 :            :                                 }
      87                 :            :                                 break;
      88                 :            :                         case 1:
      89         [ #  # ]:          0 :                                 if (ptr[0] == '.')
      90                 :          0 :                                         continue;
      91                 :            :                                 break;
      92                 :            :                         case 0:
      93                 :          0 :                                 continue;
      94                 :            :                 }
      95                 :          1 :                 normalized_path[res_len++] = '/';
      96                 :          1 :                 memcpy(normalized_path + res_len, ptr, len);
      97                 :          1 :                 res_len += len;
      98                 :            :         }
      99                 :            : 
     100         [ -  + ]:          1 :         if (res_len == 0)
     101                 :          0 :                 normalized_path[res_len++] = '/';
     102                 :          1 :         normalized_path[res_len] = '\0';
     103                 :          1 :         return normalized_path;
     104                 :            : }
     105                 :            : 
     106                 :            : /*
     107                 :            :  * Return a path name which should be used resolving parent.
     108                 :            :  * This could be different from realpath as realpath follows all symlinks.
     109                 :            :  * Function try to get a file name (even if symlink) contained in /dev/mapper.
     110                 :            :  * Symlinks are also kept if a /dev/drbd/by-res/<UUID>/<VOLUME_ID> path is used.
     111                 :            :  */
     112                 :            : char *
     113                 :          1 : canonpath(const char *path, char *resolved_path, size_t dest_size)
     114                 :            : {
     115                 :            :         static const char dev_path[] = "/dev/";
     116                 :            :         static const size_t dev_len = sizeof(dev_path) - 1;
     117                 :            : 
     118                 :            :         static const char dev_mapper_path[] = "/dev/mapper/";
     119                 :            :         static const size_t dev_mapper_len = sizeof(dev_mapper_path) - 1;
     120                 :            : 
     121                 :            :         static const char dev_drbd_res_path[] = "/dev/drbd/by-res/";
     122                 :            :         static const size_t dev_drbd_res_len = sizeof(dev_drbd_res_path) - 1;
     123                 :            : 
     124                 :            :         static const char dev_drbd_prefix[] = "/dev/drbd";
     125                 :            :         static const size_t dev_drbd_prefix_len = sizeof(dev_drbd_prefix) - 1;
     126                 :            : 
     127                 :            :         /* make some cleanup */
     128                 :          1 :         char *canon = NULL, *p, *dst;
     129                 :          1 :         size_t len = strlen(path);
     130                 :            : 
     131         [ +  - ]:          1 :         if (len >= PATH_MAX)
     132                 :            :                 goto fallback;
     133                 :            : 
     134         [ +  - ]:          1 :         if (!(canon = normalize_path(path, len)))
     135                 :          1 :                 return NULL;
     136                 :            : 
     137                 :            :         /*
     138                 :            :          * If path points to a file in /dev/mapper (with no subdirectories)
     139                 :            :          * return it without following symlinks.
     140                 :            :          */
     141 [ -  + ][ #  # ]:          1 :         if (strncmp(canon, dev_mapper_path, dev_mapper_len) == 0 &&
     142         [ #  # ]:          0 :             strchr(canon + dev_mapper_len, '/') == NULL &&
     143                 :          0 :             access(canon, F_OK) == 0) {
     144                 :          0 :                 safe_strncpy(resolved_path, canon, dest_size);
     145                 :            :                 goto end;
     146                 :            :         }
     147                 :            : 
     148                 :            :         /*
     149                 :            :          * If path is in a subdirectory of dev (possibly a logical volume)
     150                 :            :          * try to find a corresponding file in /dev/mapper and return
     151                 :            :          * if found. The path can also be a DRBD volume, try to find it in
     152                 :            :          * /dev/drbd/by-res/.
     153                 :            :          */
     154 [ -  + ][ #  # ]:          1 :         if (strncmp(canon, dev_path, dev_len) == 0 && (p = strchr(canon + dev_len, '/')) != NULL) {
     155         [ #  # ]:          0 :                 if (strchr(p+1, '/') == NULL) {
     156                 :          0 :                         safe_strncpy(resolved_path, dev_mapper_path, dest_size);
     157                 :          0 :                         dst = strchr(resolved_path, 0);
     158         [ #  # ]:          0 :                         for (p = canon + dev_len; *p; ++p) {
     159         [ #  # ]:          0 :                                 if (dst - resolved_path >= PATH_MAX - 2)
     160                 :            :                                         goto fallback;
     161      [ #  #  # ]:          0 :                                 switch (*p) {
     162                 :            :                                 case '/':
     163                 :          0 :                                         *dst = '-';
     164                 :          0 :                                         break;
     165                 :            :                                 case '-':
     166                 :          0 :                                         *dst++ = '-';
     167                 :            :                                         /* fall through */
     168                 :            :                                 default:
     169                 :          0 :                                         *dst = *p;
     170                 :            :                                 }
     171                 :          0 :                                 ++dst;
     172                 :            :                         }
     173                 :          0 :                         *dst = 0;
     174         [ #  # ]:          0 :                 } else if (strncmp(canon + dev_len, dev_drbd_res_path + dev_len, dev_drbd_res_len - dev_len) == 0) {
     175                 :            :                         /* If the path is a real DRBD path, it's a symlink that points to /dev/drbdXXXX,
     176                 :            :                          * where XXXX are digits. */
     177         [ #  # ]:          0 :                         if (!realpath(canon, resolved_path)) {
     178                 :          0 :                                 free(canon);
     179                 :          0 :                                 return NULL;
     180                 :            :                         }
     181                 :            : 
     182                 :            :                         /* Try to match /dev/drbd. */
     183         [ #  # ]:          0 :                         if (strncmp(resolved_path, dev_drbd_prefix, dev_drbd_prefix_len) != 0)
     184                 :            :                                 goto end;
     185                 :            : 
     186                 :            :                         /* Check the digits. */
     187                 :          0 :                         errno = 0;
     188                 :          0 :                         strtol(resolved_path + dev_drbd_prefix_len, &p, 10);
     189 [ #  # ][ #  # ]:          0 :                         if (p == resolved_path + dev_drbd_prefix_len || errno == ERANGE || *p != '\0')
                 [ #  # ]
     190                 :            :                                 goto end; /* Cannot parse correctly pattern. */
     191                 :            : 
     192                 :          0 :                         safe_strncpy(resolved_path, canon, dest_size);
     193                 :            :                 } else
     194                 :            :                         goto fallback;
     195         [ #  # ]:          0 :                 if (access(resolved_path, F_OK) == 0)
     196                 :            :                         goto end;
     197                 :            :         }
     198                 :            : 
     199                 :            : fallback:
     200                 :          1 :         free(canon);
     201                 :          1 :         return realpath(path, resolved_path);
     202                 :            : 
     203                 :            : end:
     204                 :          0 :         free(canon);
     205                 :          0 :         return resolved_path;
     206                 :            : }
     207                 :            : 

Generated by: LCOV version 1.13