- Updated to 3.4-rc1.
[linux-flexiantxendom0-3.2.10.git] / drivers / md / dm-table.c
index 2e227fb..8ba897f 100644 (file)
@@ -438,14 +438,18 @@ static int upgrade_mode(struct dm_dev_internal *dd, fmode_t new_mode,
 
        dd_new = dd_old = *dd;
 
-       dd_new.dm_dev.mode |= new_mode;
+       dd_new.dm_dev.mode = new_mode;
        dd_new.dm_dev.bdev = NULL;
 
        r = open_dev(&dd_new, dd->dm_dev.bdev->bd_dev, md);
-       if (r)
+       if (r == -EROFS) {
+               dd_new.dm_dev.mode &= ~FMODE_WRITE;
+               r = open_dev(&dd_new, dd->dm_dev.bdev->bd_dev, md);
+       }
+       if (!r)
                return r;
 
-       dd->dm_dev.mode |= new_mode;
+       dd->dm_dev.mode = new_mode;
        close_dev(&dd_old, md);
 
        return 0;
@@ -491,17 +495,25 @@ int dm_get_device(struct dm_target *ti, const char *path, fmode_t mode,
                dd->dm_dev.mode = mode;
                dd->dm_dev.bdev = NULL;
 
-               if ((r = open_dev(dd, dev, t->md))) {
+               r = open_dev(dd, dev, t->md);
+               if (r == -EROFS) {
+                       dd->dm_dev.mode &= ~FMODE_WRITE;
+                       r = open_dev(dd, dev, t->md);
+               }
+               if (r) {
                        kfree(dd);
                        return r;
                }
 
+               if (dd->dm_dev.mode != mode)
+                       t->mode = dd->dm_dev.mode;
+
                format_dev_t(dd->dm_dev.name, dev);
 
                atomic_set(&dd->count, 0);
                list_add(&dd->list, &t->devices);
 
-       } else if (dd->dm_dev.mode != (mode | dd->dm_dev.mode)) {
+       } else if (dd->dm_dev.mode != mode) {
                r = upgrade_mode(dd, mode, t->md);
                if (r)
                        return r;
@@ -554,9 +566,12 @@ EXPORT_SYMBOL_GPL(dm_set_device_limits);
  */
 void dm_put_device(struct dm_target *ti, struct dm_dev *d)
 {
-       struct dm_dev_internal *dd = container_of(d, struct dm_dev_internal,
-                                                 dm_dev);
+       struct dm_dev_internal *dd;
+
+       if (!d)
+               return;
 
+       dd = container_of(d, struct dm_dev_internal, dm_dev);
        if (atomic_dec_and_test(&dd->count)) {
                close_dev(dd, ti->table->md);
                list_del(&dd->list);