Print this page
OS-4319 zfs mishandles partial writes

Split Close
Expand all
Collapse all
          --- old/usr/src/uts/common/fs/zfs/zfs_vnops.c
          +++ new/usr/src/uts/common/fs/zfs/zfs_vnops.c
↓ open down ↓ 657 lines elided ↑ open up ↑
 658  658          ssize_t         start_resid = uio->uio_resid;
 659  659          ssize_t         tx_bytes;
 660  660          uint64_t        end_size;
 661  661          dmu_tx_t        *tx;
 662  662          zfsvfs_t        *zfsvfs = zp->z_zfsvfs;
 663  663          zilog_t         *zilog;
 664  664          offset_t        woff;
 665  665          ssize_t         n, nbytes;
 666  666          int             max_blksz = zfsvfs->z_max_blksz;
 667  667          int             error = 0;
      668 +        int             prev_error;
 668  669          arc_buf_t       *abuf;
 669  670          iovec_t         *aiov = NULL;
 670  671          xuio_t          *xuio = NULL;
 671  672          int             i_iov = 0;
 672  673          int             iovcnt = uio->uio_iovcnt;
 673  674          iovec_t         *iovp = uio->uio_iov;
 674  675          int             write_eof;
 675  676          int             count = 0;
 676  677          sa_bulk_attr_t  bulk[4];
 677  678          uint64_t        mtime[2], ctime[2];
↓ open down ↓ 287 lines elided ↑ open up ↑
 965  966                  zfs_tstamp_update_setup(zp, CONTENT_MODIFIED, mtime, ctime,
 966  967                      B_TRUE);
 967  968  
 968  969                  /*
 969  970                   * Update the file size (zp_size) if it has changed;
 970  971                   * account for possible concurrent updates.
 971  972                   */
 972  973                  while ((end_size = zp->z_size) < uio->uio_loffset) {
 973  974                          (void) atomic_cas_64(&zp->z_size, end_size,
 974  975                              uio->uio_loffset);
 975      -                        ASSERT(error == 0);
 976  976                  }
 977  977                  /*
 978  978                   * If we are replaying and eof is non zero then force
 979  979                   * the file size to the specified eof. Note, there's no
 980  980                   * concurrency during replay.
 981  981                   */
 982  982                  if (zfsvfs->z_replay && zfsvfs->z_replay_eof != 0)
 983  983                          zp->z_size = zfsvfs->z_replay_eof;
 984  984  
      985 +                /*
      986 +                 * Keep track of a possible pre-existing error from a partial
      987 +                 * write via dmu_write_uio_dbuf above.
      988 +                 */
      989 +                prev_error = error;
 985  990                  error = sa_bulk_update(zp->z_sa_hdl, bulk, count, tx);
 986  991  
 987  992                  zfs_log_write(zilog, tx, TX_WRITE, zp, woff, tx_bytes, ioflag);
 988  993                  dmu_tx_commit(tx);
 989  994  
 990      -                if (error != 0)
      995 +                if (prev_error != 0 || error != 0)
 991  996                          break;
 992  997                  ASSERT(tx_bytes == nbytes);
 993  998                  n -= nbytes;
 994  999  
 995 1000                  if (!xuio && n > 0)
 996 1001                          uio_prefaultpages(MIN(n, max_blksz), uio);
 997 1002          }
 998 1003  
 999 1004          rangelock_exit(lr);
1000 1005  
↓ open down ↓ 4394 lines elided ↑ open up ↑
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX