write.c 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. /* This file is the counterpart of "read.c". It contains the code for writing
  2. * insofar as this is not contained in fs_readwrite().
  3. *
  4. * The entry points into this file are
  5. * write_map: write a new zone into an inode
  6. * clear_zone: erase a zone in the middle of a file
  7. * new_block: acquire a new block
  8. * zero_block: overwrite a block with zeroes
  9. *
  10. */
  11. #include "fs.h"
  12. #include <string.h>
  13. #include <assert.h>
  14. #include <sys/param.h>
  15. #include "buf.h"
  16. #include "inode.h"
  17. #include "super.h"
  18. static void wr_indir(struct buf *bp, int index, zone_t zone);
  19. static int empty_indir(struct buf *, struct super_block *);
  20. /*===========================================================================*
  21. * write_map *
  22. *===========================================================================*/
  23. int write_map(rip, position, new_zone, op)
  24. struct inode *rip; /* pointer to inode to be changed */
  25. off_t position; /* file address to be mapped */
  26. zone_t new_zone; /* zone # to be inserted */
  27. int op; /* special actions */
  28. {
  29. /* Write a new zone into an inode.
  30. *
  31. * If op includes WMAP_FREE, free the data zone corresponding to that position
  32. * in the inode ('new_zone' is ignored then). Also free the indirect block
  33. * if that was the last entry in the indirect block.
  34. * Also free the double indirect block if that was the last entry in the
  35. * double indirect block.
  36. */
  37. int scale, ind_ex = 0, new_ind, new_dbl,
  38. zones, nr_indirects, single, zindex, ex;
  39. zone_t z, z1, z2 = NO_ZONE, old_zone;
  40. register block_t b;
  41. long excess, zone;
  42. struct buf *bp_dindir = NULL, *bp = NULL;
  43. IN_MARKDIRTY(rip);
  44. scale = rip->i_sp->s_log_zone_size; /* for zone-block conversion */
  45. /* relative zone # to insert */
  46. zone = (position/rip->i_sp->s_block_size) >> scale;
  47. zones = rip->i_ndzones; /* # direct zones in the inode */
  48. nr_indirects = rip->i_nindirs;/* # indirect zones per indirect block */
  49. /* Is 'position' to be found in the inode itself? */
  50. if (zone < zones) {
  51. zindex = (int) zone; /* we need an integer here */
  52. if(rip->i_zone[zindex] != NO_ZONE && (op & WMAP_FREE)) {
  53. free_zone(rip->i_dev, rip->i_zone[zindex]);
  54. rip->i_zone[zindex] = NO_ZONE;
  55. } else {
  56. rip->i_zone[zindex] = new_zone;
  57. }
  58. return(OK);
  59. }
  60. /* It is not in the inode, so it must be single or double indirect. */
  61. excess = zone - zones; /* first Vx_NR_DZONES don't count */
  62. new_ind = FALSE;
  63. new_dbl = FALSE;
  64. if (excess < nr_indirects) {
  65. /* 'position' can be located via the single indirect block. */
  66. z1 = rip->i_zone[zones]; /* single indirect zone */
  67. single = TRUE;
  68. } else {
  69. /* 'position' can be located via the double indirect block. */
  70. if ( (z2 = z = rip->i_zone[zones+1]) == NO_ZONE &&
  71. !(op & WMAP_FREE)) {
  72. /* Create the double indirect block. */
  73. if ( (z = alloc_zone(rip->i_dev, rip->i_zone[0])) == NO_ZONE)
  74. return(err_code);
  75. rip->i_zone[zones+1] = z;
  76. new_dbl = TRUE; /* set flag for later */
  77. }
  78. /* 'z' is zone number for double indirect block, either old
  79. * or newly created.
  80. * If there wasn't one and WMAP_FREE is set, 'z' is NO_ZONE.
  81. */
  82. excess -= nr_indirects; /* single indirect doesn't count */
  83. ind_ex = (int) (excess / nr_indirects);
  84. excess = excess % nr_indirects;
  85. if (ind_ex >= nr_indirects) return(EFBIG);
  86. if(z == NO_ZONE && (op & WMAP_FREE)) {
  87. /* WMAP_FREE and no double indirect block - then no
  88. * single indirect block either.
  89. */
  90. z1 = NO_ZONE;
  91. } else {
  92. b = (block_t) z << scale;
  93. bp_dindir = get_block(rip->i_dev, b,
  94. (new_dbl?NO_READ:NORMAL));
  95. if (new_dbl) zero_block(bp_dindir);
  96. z1 = rd_indir(bp_dindir, ind_ex);
  97. }
  98. single = FALSE;
  99. }
  100. /* z1 is now single indirect zone, or NO_ZONE; 'excess' is index.
  101. * We have to create the indirect zone if it's NO_ZONE. Unless
  102. * we're freeing (WMAP_FREE).
  103. */
  104. if (z1 == NO_ZONE && !(op & WMAP_FREE)) {
  105. z1 = alloc_zone(rip->i_dev, rip->i_zone[0]);
  106. if (single)
  107. rip->i_zone[zones] = z1; /* update inode w. single indirect */
  108. else
  109. wr_indir(bp_dindir, ind_ex, z1); /* update dbl indir */
  110. new_ind = TRUE;
  111. /* If double ind, it is dirty. */
  112. if (bp_dindir != NULL) MARKDIRTY(bp_dindir);
  113. if (z1 == NO_ZONE) {
  114. /* Release dbl indirect blk. */
  115. put_block(bp_dindir);
  116. return(err_code); /* couldn't create single ind */
  117. }
  118. }
  119. /* z1 is indirect block's zone number (unless it's NO_ZONE when we're
  120. * freeing).
  121. */
  122. if(z1 != NO_ZONE) {
  123. ex = (int) excess; /* we need an int here */
  124. b = (block_t) z1 << scale;
  125. bp = get_block(rip->i_dev, b, (new_ind ? NO_READ : NORMAL) );
  126. if (new_ind) zero_block(bp);
  127. if(op & WMAP_FREE) {
  128. if((old_zone = rd_indir(bp, ex)) != NO_ZONE) {
  129. free_zone(rip->i_dev, old_zone);
  130. wr_indir(bp, ex, NO_ZONE);
  131. }
  132. /* Last reference in the indirect block gone? Then
  133. * free the indirect block.
  134. */
  135. if(empty_indir(bp, rip->i_sp)) {
  136. free_zone(rip->i_dev, z1);
  137. z1 = NO_ZONE;
  138. /* Update the reference to the indirect block to
  139. * NO_ZONE - in the double indirect block if there
  140. * is one, otherwise in the inode directly.
  141. */
  142. if(single) {
  143. rip->i_zone[zones] = z1;
  144. } else {
  145. wr_indir(bp_dindir, ind_ex, z1);
  146. MARKDIRTY(bp_dindir);
  147. }
  148. }
  149. } else {
  150. wr_indir(bp, ex, new_zone);
  151. }
  152. /* z1 equals NO_ZONE only when we are freeing up the indirect block. */
  153. if(z1 != NO_ZONE) MARKDIRTY(bp);
  154. put_block(bp);
  155. }
  156. /* If the single indirect block isn't there (or was just freed),
  157. * see if we have to keep the double indirect block, if any.
  158. * If we don't have to keep it, don't bother writing it out.
  159. */
  160. if(z1 == NO_ZONE && !single && z2 != NO_ZONE &&
  161. empty_indir(bp_dindir, rip->i_sp)) {
  162. free_zone(rip->i_dev, z2);
  163. rip->i_zone[zones+1] = NO_ZONE;
  164. }
  165. put_block(bp_dindir); /* release double indirect blk */
  166. return(OK);
  167. }
  168. /*===========================================================================*
  169. * wr_indir *
  170. *===========================================================================*/
  171. static void wr_indir(bp, index, zone)
  172. struct buf *bp; /* pointer to indirect block */
  173. int index; /* index into *bp */
  174. zone_t zone; /* zone to write */
  175. {
  176. /* Given a pointer to an indirect block, write one entry. */
  177. struct super_block *sp;
  178. if(bp == NULL)
  179. panic("wr_indir() on NULL");
  180. sp = &superblock;
  181. /* write a zone into an indirect block */
  182. assert(sp->s_version == V3);
  183. b_v2_ind(bp)[index] = (zone_t) conv4(sp->s_native, (long) zone);
  184. }
  185. /*===========================================================================*
  186. * empty_indir *
  187. *===========================================================================*/
  188. static int empty_indir(bp, sb)
  189. struct buf *bp; /* pointer to indirect block */
  190. struct super_block *sb; /* superblock of device block resides on */
  191. {
  192. /* Return nonzero if the indirect block pointed to by bp contains
  193. * only NO_ZONE entries.
  194. */
  195. unsigned int i;
  196. for(i = 0; i < V2_INDIRECTS(sb->s_block_size); i++)
  197. if( b_v2_ind(bp)[i] != NO_ZONE)
  198. return(0);
  199. return(1);
  200. }
  201. /*===========================================================================*
  202. * clear_zone *
  203. *===========================================================================*/
  204. void clear_zone(rip, pos, flag)
  205. register struct inode *rip; /* inode to clear */
  206. off_t __unused pos; /* points to block to clear */
  207. int __unused flag; /* 1 if called by new_block, 0 otherwise */
  208. {
  209. /* Zero a zone, possibly starting in the middle. The parameter 'pos' gives
  210. * a byte in the first block to be zeroed. Clearzone() is called from
  211. * fs_readwrite(), truncate_inode(), and new_block().
  212. */
  213. int scale;
  214. /* If the block size and zone size are the same, clear_zone() not needed. */
  215. scale = rip->i_sp->s_log_zone_size;
  216. assert(scale == 0);
  217. return;
  218. }
  219. /*===========================================================================*
  220. * new_block *
  221. *===========================================================================*/
  222. struct buf *new_block(rip, position)
  223. register struct inode *rip; /* pointer to inode */
  224. off_t position; /* file pointer */
  225. {
  226. /* Acquire a new block and return a pointer to it. Doing so may require
  227. * allocating a complete zone, and then returning the initial block.
  228. * On the other hand, the current zone may still have some unused blocks.
  229. */
  230. struct buf *bp;
  231. block_t b, base_block;
  232. zone_t z;
  233. zone_t zone_size;
  234. int scale, r;
  235. /* Is another block available in the current zone? */
  236. if ( (b = read_map(rip, position, 0)) == NO_BLOCK) {
  237. if (rip->i_zsearch == NO_ZONE) {
  238. /* First search for this file. Start looking from
  239. * the file's first data zone to prevent fragmentation
  240. */
  241. if ( (z = rip->i_zone[0]) == NO_ZONE) {
  242. /* No first zone for file either, let alloc_zone
  243. * decide. */
  244. z = (zone_t) rip->i_sp->s_firstdatazone;
  245. }
  246. } else {
  247. /* searched before, start from last find */
  248. z = rip->i_zsearch;
  249. }
  250. if ( (z = alloc_zone(rip->i_dev, z)) == NO_ZONE) return(NULL);
  251. rip->i_zsearch = z; /* store for next lookup */
  252. if ( (r = write_map(rip, position, z, 0)) != OK) {
  253. free_zone(rip->i_dev, z);
  254. err_code = r;
  255. return(NULL);
  256. }
  257. /* If we are not writing at EOF, clear the zone, just to be safe. */
  258. if ( position != rip->i_size) clear_zone(rip, position, 1);
  259. scale = rip->i_sp->s_log_zone_size;
  260. base_block = (block_t) z << scale;
  261. zone_size = (zone_t) rip->i_sp->s_block_size << scale;
  262. b = base_block + (block_t)((position % zone_size)/rip->i_sp->s_block_size);
  263. }
  264. r = lmfs_get_block_ino(&bp, rip->i_dev, b, NO_READ, rip->i_num,
  265. rounddown(position, rip->i_sp->s_block_size));
  266. if (r != OK)
  267. panic("MFS: error getting block (%llu,%u): %d", rip->i_dev, b, r);
  268. zero_block(bp);
  269. return(bp);
  270. }
  271. /*===========================================================================*
  272. * zero_block *
  273. *===========================================================================*/
  274. void zero_block(bp)
  275. register struct buf *bp; /* pointer to buffer to zero */
  276. {
  277. /* Zero a block. */
  278. ASSERT(bp->data);
  279. memset(b_data(bp), 0, lmfs_fs_block_size());
  280. MARKDIRTY(bp);
  281. }