]> git.ipfire.org Git - thirdparty/mdadm.git/blobdiff - sysfs.c
Grow: replace '1' with 'INVALID_SECTORS' where appropriate.
[thirdparty/mdadm.git] / sysfs.c
diff --git a/sysfs.c b/sysfs.c
index f1c66692019a9fb970f3d1b49a54a1d9f5e0d2af..6bc28651fd4f8f81c582eb4d5e98b4de80cdc90a 100644 (file)
--- a/sysfs.c
+++ b/sysfs.c
@@ -57,16 +57,12 @@ void sysfs_free(struct mdinfo *sra)
        }
 }
 
-int sysfs_open(int devnum, char *devname, char *attr)
+int sysfs_open(char *devnm, char *devname, char *attr)
 {
        char fname[50];
        int fd;
-       char *mdname = devnum2devname(devnum);
 
-       if (!mdname)
-               return -1;
-
-       sprintf(fname, "/sys/block/%s/md/", mdname);
+       sprintf(fname, "/sys/block/%s/md/", devnm);
        if (devname) {
                strcat(fname, devname);
                strcat(fname, "/");
@@ -75,26 +71,25 @@ int sysfs_open(int devnum, char *devname, char *attr)
        fd = open(fname, O_RDWR);
        if (fd < 0 && errno == EACCES)
                fd = open(fname, O_RDONLY);
-       free(mdname);
        return fd;
 }
 
-void sysfs_init(struct mdinfo *mdi, int fd, int devnum)
+void sysfs_init(struct mdinfo *mdi, int fd, char *devnm)
 {
        mdi->sys_name[0] = 0;
        if (fd >= 0) {
                mdu_version_t vers;
                if (ioctl(fd, RAID_VERSION, &vers) != 0)
                        return;
-               devnum = fd2devnum(fd);
+               devnm = fd2devnm(fd);
        }
-       if (devnum == NoMdDev)
+       if (devnm == NULL)
                return;
-       fmt_devname(mdi->sys_name, devnum);
+       strcpy(mdi->sys_name, devnm);
 }
 
 
-struct mdinfo *sysfs_read(int fd, int devnum, unsigned long options)
+struct mdinfo *sysfs_read(int fd, char *devnm, unsigned long options)
 {
        char fname[PATH_MAX];
        char buf[PATH_MAX];
@@ -105,11 +100,8 @@ struct mdinfo *sysfs_read(int fd, int devnum, unsigned long options)
        DIR *dir = NULL;
        struct dirent *de;
 
-       sra = malloc(sizeof(*sra));
-       if (sra == NULL)
-               return sra;
-       memset(sra, 0, sizeof(*sra));
-       sysfs_init(sra, fd, devnum);
+       sra = xcalloc(1, sizeof(*sra));
+       sysfs_init(sra, fd, devnm);
        if (sra->sys_name[0] == 0) {
                free(sra);
                return NULL;
@@ -179,8 +171,10 @@ struct mdinfo *sysfs_read(int fd, int devnum, unsigned long options)
        if (options & GET_CACHE) {
                strcpy(base, "stripe_cache_size");
                if (load_sys(fname, buf))
-                       goto abort;
-               sra->cache_size = strtoul(buf, NULL, 0);
+                       /* Probably level doesn't support it */
+                       sra->cache_size = 0;
+               else
+                       sra->cache_size = strtoul(buf, NULL, 0);
        }
        if (options & GET_MISMATCH) {
                strcpy(base, "mismatch_cnt");
@@ -217,6 +211,19 @@ struct mdinfo *sysfs_read(int fd, int devnum, unsigned long options)
                msec = (msec * 1000) / scale;
                sra->safe_mode_delay = msec;
        }
+       if (options & GET_BITMAP_LOCATION) {
+               strcpy(base, "bitmap/location");
+               if (load_sys(fname, buf))
+                       goto abort;
+               if (strncmp(buf, "file", 4) == 0)
+                       sra->bitmap_offset = 1;
+               else if (strncmp(buf, "none", 4) == 0)
+                       sra->bitmap_offset = 0;
+               else if (buf[0] == '+')
+                       sra->bitmap_offset = strtol(buf+1, NULL, 10);
+               else
+                       goto abort;
+       }
 
        if (! (options & GET_DEVS))
                return sra;
@@ -237,9 +244,7 @@ struct mdinfo *sysfs_read(int fd, int devnum, unsigned long options)
                dbase = base + strlen(base);
                *dbase++ = '/';
 
-               dev = malloc(sizeof(*dev));
-               if (!dev)
-                       goto abort;
+               dev = xmalloc(sizeof(*dev));
 
                /* Always get slot, major, minor */
                strcpy(dbase, "slot");
@@ -275,6 +280,7 @@ struct mdinfo *sysfs_read(int fd, int devnum, unsigned long options)
                        free(dev);
                        continue;
                }
+               sra->array.nr_disks++;
                sscanf(buf, "%d:%d", &dev->disk.major, &dev->disk.minor);
 
                /* special case check for block devices that can go 'offline' */
@@ -294,6 +300,11 @@ struct mdinfo *sysfs_read(int fd, int devnum, unsigned long options)
                        if (load_sys(fname, buf))
                                goto abort;
                        dev->data_offset = strtoull(buf, NULL, 0);
+                       strcpy(dbase, "new_offset");
+                       if (load_sys(fname, buf) == 0)
+                               dev->new_data_offset = strtoull(buf, NULL, 0);
+                       else
+                               dev->new_data_offset = dev->data_offset;
                }
                if (options & GET_SIZE) {
                        strcpy(dbase, "size");
@@ -379,7 +390,7 @@ unsigned long long get_component_size(int fd)
                return 0;
        n = read(fd, fname, sizeof(fname));
        close(fd);
-       if (n == sizeof(fname))
+       if (n < 0 || n == sizeof(fname))
                return 0;
        fname[n] = 0;
        return strtoull(fname, NULL, 10) * 2;
@@ -415,6 +426,14 @@ int sysfs_set_num(struct mdinfo *sra, struct mdinfo *dev,
        return sysfs_set_str(sra, dev, name, valstr);
 }
 
+int sysfs_set_num_signed(struct mdinfo *sra, struct mdinfo *dev,
+                        char *name, long long val)
+{
+       char valstr[50];
+       sprintf(valstr, "%lli", val);
+       return sysfs_set_str(sra, dev, name, valstr);
+}
+
 int sysfs_uevent(struct mdinfo *sra, char *event)
 {
        char fname[50];
@@ -428,6 +447,11 @@ int sysfs_uevent(struct mdinfo *sra, char *event)
                return -1;
        n = write(fd, event, strlen(event));
        close(fd);
+       if (n != (int)strlen(event)) {
+               dprintf(Name ": failed to write '%s' to '%s' (%s)\n",
+                       event, fname, strerror(errno));
+               return -1;
+       }
        return 0;
 }      
 
@@ -465,7 +489,7 @@ int sysfs_fd_get_ll(int fd, unsigned long long *val)
        lseek(fd, 0, 0);
        n = read(fd, buf, sizeof(buf));
        if (n <= 0)
-               return -1;
+               return -2;
        buf[n] = 0;
        *val = strtoull(buf, &ep, 0);
        if (ep == buf || (*ep != 0 && *ep != '\n' && *ep != ' '))
@@ -536,12 +560,25 @@ int sysfs_set_array(struct mdinfo *info, int vers)
        ver[0] = 0;
        if (info->array.major_version == -1 &&
            info->array.minor_version == -2) {
+               char buf[1024];
+
                strcat(strcpy(ver, "external:"), info->text_version);
 
+               /* meta version might already be set if we are setting
+                * new geometry for a reshape.  In that case we don't
+                * want to over-write the 'readonly' flag that is
+                * stored in the metadata version.  So read the current
+                * version first, and preserve the flag
+                */
+               if (sysfs_get_str(info, NULL, "metadata_version",
+                                 buf, 1024) > 0)
+                       if (strlen(buf) >= 9 && buf[9] == '-')
+                               ver[9] = '-';
+
                if ((vers % 100) < 2 ||
                    sysfs_set_str(info, NULL, "metadata_version",
                                  ver) < 0) {
-                       fprintf(stderr, Name ": This kernel does not "
+                       pr_err("This kernel does not "
                                "support external metadata.\n");
                        return 1;
                }
@@ -562,7 +599,7 @@ int sysfs_set_array(struct mdinfo *info, int vers)
                rc = sysfs_set_num(info, NULL, "array_size",
                                   info->custom_array_size/2);
                if (rc && errno == ENOENT) {
-                       fprintf(stderr, Name ": This kernel does not "
+                       pr_err("This kernel does not "
                                "have the md/array_size attribute, "
                                "the array may be larger than expected\n");
                        rc = 0;
@@ -600,13 +637,7 @@ int sysfs_add_disk(struct mdinfo *sra, struct mdinfo *sd, int resume)
                return rv;
 
        memset(nm, 0, sizeof(nm));
-       sprintf(dv, "/sys/dev/block/%d:%d", sd->disk.major, sd->disk.minor);
-       rv = readlink(dv, nm, sizeof(nm));
-       if (rv <= 0)
-               return -1;
-       nm[rv] = '\0';
-       dname = strrchr(nm, '/');
-       if (dname) dname++;
+       dname = devid2devnm(makedev(sd->disk.major, sd->disk.minor));
        strcpy(sd->sys_name, "dev-");
        strcpy(sd->sys_name+4, dname);
 
@@ -704,9 +735,9 @@ int sysfs_disk_to_scsi_id(int fd, __u32 *id)
        /* from an open block device, try to retrieve it scsi_id */
        struct stat st;
        char path[256];
-       char *c1, *c2;
        DIR *dir;
        struct dirent *de;
+       int host, bus, target, lun;
 
        if (fstat(fd, &st))
                return 1;
@@ -718,61 +749,53 @@ int sysfs_disk_to_scsi_id(int fd, __u32 *id)
        if (!dir)
                return 1;
 
-       de = readdir(dir);
-       while (de) {
-               if (strchr(de->d_name, ':'))
+       for (de = readdir(dir); de; de = readdir(dir)) {
+               int count;
+
+               if (de->d_type != DT_DIR)
+                       continue;
+
+               count = sscanf(de->d_name, "%d:%d:%d:%d", &host, &bus, &target, &lun);
+               if (count == 4)
                        break;
-               de = readdir(dir);
        }
        closedir(dir);
 
        if (!de)
                return 1;
 
-       c1 = de->d_name;
-       c2 = strchr(c1, ':');
-       *c2 = '\0';
-       *id = strtol(c1, NULL, 10) << 24; /* host */
-       c1 = c2 + 1;
-       c2 = strchr(c1, ':');
-       *c2 = '\0';
-       *id |= strtol(c1, NULL, 10) << 16; /* bus */
-       c1 = c2 + 1;
-       c2 = strchr(c1, ':');
-       *c2 = '\0';
-       *id |= strtol(c1, NULL, 10) << 8; /* target */
-       c1 = c2 + 1;
-       *id |= strtol(c1, NULL, 10); /* lun */
-
+       *id = (host << 24) | (bus << 16) | (target << 8) | (lun << 0);
        return 0;
 }
 
 
-int sysfs_unique_holder(int devnum, long rdev)
+int sysfs_unique_holder(char *devnm, long rdev)
 {
-       /* Check that devnum is a holder of rdev,
+       /* Check that devnm is a holder of rdev,
         * and is the only holder.
         * we should be locked against races by
-        * an O_EXCL on devnum
+        * an O_EXCL on devnm
+        * Return values:
+        *  0 - not unique, not even a holder
+        *  1 - unique, this is the only holder.
+        *  2/3 - not unique, there is another holder
+        * -1 - error, cannot find the holders
         */
        DIR *dir;
        struct dirent *de;
        char dirname[100];
        char l;
-       int found = 0;
+       int ret = 0;
        sprintf(dirname, "/sys/dev/block/%d:%d/holders",
                major(rdev), minor(rdev));
        dir = opendir(dirname);
-       errno = ENOENT;
        if (!dir)
-               return 0;
+               return -1;
        l = strlen(dirname);
        while ((de = readdir(dir)) != NULL) {
-               char buf[10];
+               char buf[100];
+               char *sl;
                int n;
-               int mj, mn;
-               char c;
-               int fd;
 
                if (de->d_ino == 0)
                        continue;
@@ -780,41 +803,28 @@ int sysfs_unique_holder(int devnum, long rdev)
                        continue;
                strcpy(dirname+l, "/");
                strcat(dirname+l, de->d_name);
-               strcat(dirname+l, "/dev");
-               fd = open(dirname, O_RDONLY);
-               if (fd < 0) {
-                       errno = ENOENT;
-                       break;
-               }
-               n = read(fd, buf, sizeof(buf)-1);
-               close(fd);
+               n = readlink(dirname, buf, sizeof(buf)-1);
+               if (n <= 0)
+                       continue;
                buf[n] = 0;
-               if (sscanf(buf, "%d:%d%c", &mj, &mn, &c) != 3 ||
-                   c != '\n') {
-                       errno = ENOENT;
-                       break;
-               }
-               if (mj != MD_MAJOR)
-                       mn = -1-(mn>>6);
+               sl = strrchr(buf, '/');
+               if (!sl)
+                       continue;
+               sl++;
 
-               if (devnum != mn) {
-                       errno = EEXIST;
-                       break;
-               }
-               found = 1;
+               if (strcmp(devnm, sl) == 0)
+                       ret |= 1;
+               else
+                       ret |= 2;
        }
        closedir(dir);
-       if (de)
-               return 0;
-       else
-               return found;
+       return ret;
 }
 
 int sysfs_freeze_array(struct mdinfo *sra)
 {
        /* Try to freeze resync/rebuild on this array/container.
         * Return -1 if the array is busy,
-        * return -2 container cannot be frozen,
         * return 0 if this kernel doesn't support 'frozen'
         * return 1 if it worked.
         */
@@ -824,8 +834,10 @@ int sysfs_freeze_array(struct mdinfo *sra)
                return 1; /* no sync_action == frozen */
        if (sysfs_get_str(sra, NULL, "sync_action", buf, 20) <= 0)
                return 0;
-       if (strcmp(buf, "idle\n") != 0 &&
-           strcmp(buf, "frozen\n") != 0)
+       if (strcmp(buf, "frozen\n") == 0)
+               /* Already frozen */
+               return 0;
+       if (strcmp(buf, "idle\n") != 0)
                return -1;
        if (sysfs_set_str(sra, NULL, "sync_action", "frozen") < 0)
                return 0;